]> git.sesse.net Git - mlt/commitdiff
Add rotoscoping filter (WIP):
authorTill Theato <root@ttill.de>
Sat, 15 Jan 2011 21:09:41 +0000 (22:09 +0100)
committerTill Theato <root@ttill.de>
Sat, 15 Jan 2011 21:09:41 +0000 (22:09 +0100)
It hides everything not in the polygon defined by the vertices given
through the "polygon" parameter

src/modules/rotoscoping/Makefile [new file with mode: 0644]
src/modules/rotoscoping/factory.c [new file with mode: 0644]
src/modules/rotoscoping/filter_rotoscoping.c [new file with mode: 0644]

diff --git a/src/modules/rotoscoping/Makefile b/src/modules/rotoscoping/Makefile
new file mode 100644 (file)
index 0000000..bd1025f
--- /dev/null
@@ -0,0 +1,36 @@
+CFLAGS += -I../..
+
+LDFLAGS += -L../../framework -lmlt -lm
+
+include ../../../config.mak
+
+TARGET = ../libmltrotoscoping$(LIBSUF)
+
+OBJS = factory.o \
+          filter_rotoscoping.o
+
+
+LDFLAGS += -lm
+
+SRCS := $(OBJS:.o=.c)
+
+all:   $(TARGET)
+
+$(TARGET): $(OBJS)
+               $(CC) $(SHFLAGS) -o $@ $(OBJS) $(LDFLAGS)
+
+depend:        $(SRCS)
+               $(CC) -MM $(CFLAGS) $^ 1>.depend
+
+distclean:     clean
+               rm -f .depend
+
+clean: 
+               rm -f $(OBJS) $(TARGET)
+
+install: all
+       install -m 755 $(TARGET) "$(DESTDIR)$(libdir)/mlt"
+
+ifneq ($(wildcard .depend),)
+include .depend
+endif
diff --git a/src/modules/rotoscoping/factory.c b/src/modules/rotoscoping/factory.c
new file mode 100644 (file)
index 0000000..04c5d85
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * factory.c -- the factory method interfaces
+ * Copyright (C) 2003-2004 Ushodaya Enterprises Limited
+ * Author: Charles Yates <charles.yates@pandora.be>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <string.h>
+#include <framework/mlt.h>
+
+extern mlt_filter filter_rotoscoping_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg );
+
+MLT_REPOSITORY
+{
+        MLT_REGISTER( filter_type, "rotoscoping", filter_rotoscoping_init );
+}
diff --git a/src/modules/rotoscoping/filter_rotoscoping.c b/src/modules/rotoscoping/filter_rotoscoping.c
new file mode 100644 (file)
index 0000000..e25e6b6
--- /dev/null
@@ -0,0 +1,205 @@
+/*
+ * rotoscoping.c -- (not yet)keyframable vector based rotoscoping
+ * Copyright (C) 2011 Till Theato <root@ttill.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <framework/mlt_filter.h>
+#include <framework/mlt_frame.h>
+#include <framework/mlt_tokeniser.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <string.h>
+
+#define MAX( x, y ) x > y ? x : y
+#define MIN( x, y ) x < y ? x : y
+
+/** x, y pair with double precision */
+typedef struct PointF
+{
+    double x;
+    double y;
+} PointF;
+
+/**
+ * Extracts the polygon points stored in \param string
+ * \param string string containing the points in the format: x1;y1#x2;y2#...
+ * \param points pointer to array of points. Will be allocated and filled with the points in \param string
+ * \return number of points
+ */
+int parsePolygonString( char *string, PointF **points )
+{
+    // Create a tokeniser
+    mlt_tokeniser tokens = mlt_tokeniser_init( );
+
+    // Tokenise
+    if ( string != NULL )
+        mlt_tokeniser_parse_new( tokens, string, "#" );
+
+    // Iterate through each token
+    int count = mlt_tokeniser_count( tokens );
+    *points = malloc( (count + 1) * sizeof( struct PointF ) );
+    int i;
+    for ( i = 0; i < count; ++i )
+    {
+        char *value = mlt_tokeniser_get_string( tokens, i );
+        (*points)[i].x = atof( value );
+        (*points)[i].y = atof( strchr( value, ';' ) + 1 );
+    }
+
+    // Remove the tokeniser
+    mlt_tokeniser_close( tokens );
+
+    return count;
+}
+
+/** helper for using qsort with an array of integers. */
+int ncompare( const void *a, const void *b )
+{
+    return *(const int*)a - *(const int*)b;
+}
+
+/**
+ * Determines which points are located in the polygon and sets their value in \param map to 1
+ * \param vertices points defining the polygon
+ * \param count number of vertices
+ * \param with x range
+ * \param height y range
+ * \param map array of integers of the dimension width * height.
+ *            The map entries belonging to the points in the polygon will be set to 1, the other entries remain untouched.
+ * 
+ * based on public-domain code by Darel Rex Finley, 2007
+ * \see: http://alienryderflex.com/polygon_fill/
+ */
+void fillMap( PointF *vertices, int count, int width, int height, int *map )
+{
+    int nodes, nodeX[1024], pixelX, pixelY, i, j, swap;
+    
+    // Loop through the rows of the image
+    for ( pixelY = 0; pixelY < height; pixelY++ )
+    {
+        /*
+         * Build a list of nodes.
+         * nodes are located at the borders of the polygon
+         * and therefore indicate a move from in to out or vice versa
+         */
+        nodes = 0;
+        for ( i = 0, j = count - 1; i < count; j = i++ )
+            if ( (vertices[i].y > (double)pixelY) != (vertices[j].y > (double)pixelY) )
+                nodeX[nodes++] = (int)(vertices[i].x + (pixelY - vertices[i].y) / (vertices[j].y - vertices[i].y) * (vertices[j].x - vertices[i].x) );
+
+        qsort( nodeX, nodes, sizeof( int ), ncompare );
+
+        // Fill the pixels between node pairs.
+        for ( i = 0; i < nodes; i += 2 )
+        {
+            if ( nodeX[i] >= width )
+                break;
+            if ( nodeX[i+1] > 0 )
+            {
+                nodeX[i] = MAX( 0, nodeX[i] );
+                nodeX[i+1] = MIN( nodeX[i+1], width );
+                for ( j = nodeX[i]; j < nodeX[i+1]; j++ )
+                    map[width * pixelY + j] = 1;
+            }
+        }
+    }
+}
+
+/** Do it :-).
+*/
+static int filter_get_image( mlt_frame this, uint8_t **image, mlt_image_format *format, int *width, int *height, int writable )
+{
+    // Get the image
+    *format = mlt_image_rgb24a;
+    int error = mlt_frame_get_image( this, image, format, width, height, 1 );
+
+    // Only process if we have no error and a valid colour space
+    if ( !error )
+    {
+        struct PointF *points;
+        int length, count;
+        points = mlt_properties_get_data( MLT_FRAME_PROPERTIES( this ), "points", &length );
+        count = length / sizeof( struct PointF );
+
+        int i;
+        for ( i = 0; i < count; ++i ) {
+            points[i].x *= *width;
+            points[i].y *= *height;
+        }
+
+        if ( count )
+        {
+            int *map = calloc( *width * *height, sizeof( int ) );
+            fillMap( points, count, *width, *height, map );
+
+            uint8_t *p = *image;
+            uint8_t *q = *image + *width * *height * 4;
+
+            while ( p != q )
+            {
+                int pixel = (p - *image) / 4;
+                if ( !map[pixel] ) {
+                    // pixel is not in polygon
+                    // => set to black
+                    p[0] = p[1] = p[2] = 0;
+                }
+                p += 4;
+            }
+
+            free( map );
+        }
+    }
+
+    return error;
+}
+
+/** Filter processing.
+*/
+static mlt_frame filter_process( mlt_filter this, mlt_frame frame )
+{
+    char *polygon = mlt_properties_get( MLT_FILTER_PROPERTIES( this ), "polygon" );
+
+    if ( polygon == NULL || strcmp( polygon, "" ) == 0 ) {
+        // :( we are redundant
+        return frame;
+    }
+
+    struct PointF *points;
+    int count = parsePolygonString( polygon, &points );
+    int length = count * sizeof( struct PointF );
+
+    mlt_properties_set_data( MLT_FRAME_PROPERTIES( frame ), "points", points, length, free, NULL );
+    mlt_frame_push_get_image( frame, filter_get_image );
+
+    return frame;
+}
+
+/** Constructor for the filter.
+*/
+mlt_filter filter_rotoscoping_init( mlt_profile profile, mlt_service_type type, const char *id, char *arg )
+{
+        mlt_filter this = mlt_filter_new( );
+        if ( this != NULL )
+        {
+                this->process = filter_process;
+                if ( arg != NULL )
+                    mlt_properties_set( MLT_FILTER_PROPERTIES( this ), "polygon", arg );
+        }
+        return this;
+}