Skip to content
This repository has been archived by the owner on Aug 16, 2023. It is now read-only.

Commit

Permalink
version 0.2.1.dev0
Browse files Browse the repository at this point in the history
  • Loading branch information
dthpham committed Apr 3, 2016
1 parent 1c3290a commit 8c9abe2
Show file tree
Hide file tree
Showing 34 changed files with 1,186 additions and 1,893 deletions.
2 changes: 0 additions & 2 deletions .gitattributes

This file was deleted.

9 changes: 5 additions & 4 deletions .gitignore
Expand Up @@ -8,15 +8,15 @@

# misc
*~

# de
*.DS_Store
*.directory

# virtualenv
bin
lib
include
Scripts
env

# pip
pip-selfcheck.json
Expand All @@ -35,10 +35,11 @@ __pycache__
.vs
*.pyproj*
*.sln
Scripts
env

# development
tags
*.pth
dev_settings.py
output.mp4
clear.sh
out.mp4
45 changes: 17 additions & 28 deletions LICENSE
@@ -1,32 +1,21 @@
Copyright (c) 2015 by Duong Pham
The MIT License (MIT)

All rights reserved.
Copyright (c) 2016 Duong Pham

Redistribution and use in source and binary forms of the software as well
as documentation, with or without modification, are permitted provided
that the following conditions are met:
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials provided
with the distribution.

* The names of the contributors may not be used to endorse or
promote products derived from this software without specific
prior written permission.

THIS SOFTWARE AND DOCUMENTATION IS PROVIDED BY THE COPYRIGHT HOLDERS AND
CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT
NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE AND DOCUMENTATION, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
DAMAGE.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
6 changes: 2 additions & 4 deletions MANIFEST.in
@@ -1,10 +1,8 @@
recursive-include tests *
include README.md LICENSE
recursive-include tests *.py
recursive-exclude tests *.pyc
recursive-exclude vendor *
recursive-include vendor/opencv-ndarray-conversion *
include butterflow/avinfo.c
include butterflow/ocl.c
include butterflow/motion.cpp
exclude butterflow/dev_settings.py
include LICENSE
include README.md
35 changes: 21 additions & 14 deletions README.md
@@ -1,16 +1,23 @@
# Butterflow
*Butterflow* is an easy to use command-line tool that lets you create fluid slow
motion and motion interpolated videos.
*Butterflow* is a command-line tool that lets you make fluid slow motion and
motion interpolated videos.

## How does it work?
It works by rendering intermediate frames between existing frames. For example,
given two existing frames, `A` and `B`, this program can generate frames `C.1`,
`C.2`...`C.n` that are positioned between the two. This process, called
[motion interpolation](http://en.wikipedia.org/wiki/Motion_interpolation),
increases frame rates and can give the perception of smoother motion and more
fluid animation, an effect most people know as the "soap opera effect".
Butterflow takes advantage of newly-available frames to make high speed, slow
motion videos with minimal judder.
It works by rendering intermediate frames between existing frames using a
process called [motion interpolation](http://en.wikipedia.org/wiki/Motion_interpolation).
For example, given two existing frames, `A` and `B`, this program can generate
frames `C.1`, `C.2`...`C.n` that are positioned between the two. In contrast
to other tools that can only blend or dupe frames, this program warps pixels
based on motion to generate new ones.

Butterflow uses these interpolated frames to increase a video's frame rate,
which can give the perception of smoother motion and more fluid animation, an
effect that most people know as the "soap opera effect".

## Demonstration

This is a demonstration of Butterflow leveraging motion interpolation to make
slow motion videos with minimal judder.

![](http://srv.dthpham.me/static/ink.gif)

Expand Down Expand Up @@ -44,10 +51,10 @@ Refer to the
for instructions.

## Setup
Butterflow requires no additional setup to use, but it's too slow out of the box
to do any serious work, so you need to set up a functional OpenCL environment on
your machine to take advantage of hardware accelerated methods that will make
rendering significantly faster.
Butterflow requires no additional setup to use, however it's too slow out of
the box to do any serious work. It's recommended that you set up a functional
OpenCL environment on your machine to take advantage of hardware accelerated
methods that will make rendering significantly faster.

See [Setting up OpenCL](https://github.com/dthpham/butterflow/blob/master/docs/Setting-Up-OpenCL.md)
for details on how to do this.
Expand Down
4 changes: 1 addition & 3 deletions butterflow/__main__.py
@@ -1,8 +1,6 @@
#!/usr/bin/env python2
# console scripts entry point into butterflow

import sys

if __name__ == '__main__':
import sys
from butterflow.cli import main
sys.exit(main())
43 changes: 23 additions & 20 deletions butterflow/avinfo.c
@@ -1,8 +1,9 @@
// retrieves media file information
// get av file info

#include <Python.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libavutil/avutil.h>
Expand All @@ -14,12 +15,17 @@
PyErr_SetString(PyExc_RuntimeError, "set dict failed"); \
return (PyObject*)NULL; }
#define MS_PER_SEC 1000
#define EPSILON 1.0e-8


int gcd(int a, int b) {
return b == 0 ? a : gcd(b, a % b);
}

int almost_equal(double a, double b) {
return fabs(a - b) <= EPSILON;
}

static PyObject*
get_av_info(PyObject *self, PyObject *arg) {
char *path = PyString_AsString(arg);
Expand All @@ -29,20 +35,19 @@ get_av_info(PyObject *self, PyObject *arg) {

AVFormatContext *format_ctx = avformat_alloc_context();

/* make quiet. should reset when finished */
int initial_level = av_log_get_level();
av_log_set_level(AV_LOG_ERROR);
av_log_set_level(AV_LOG_ERROR); /* make quiet */

int rc = avformat_open_input(&format_ctx, path, NULL, NULL);
if (rc != 0) {
PyErr_SetString(PyExc_RuntimeError, "could not open file");
PyErr_SetString(PyExc_RuntimeError, "open input failed");
return (PyObject*)NULL;
}

rc = avformat_find_stream_info(format_ctx, NULL);
if (rc < 0) {
avformat_close_input(&format_ctx);
PyErr_SetString(PyExc_RuntimeError, "could not find stream info");
PyErr_SetString(PyExc_RuntimeError, "no stream info found");
return (PyObject*)NULL;
}

Expand Down Expand Up @@ -77,7 +82,8 @@ get_av_info(PyObject *self, PyObject *arg) {
unsigned long frames = 0;
AVRational sar = {0, 0}; /* sample aspect ratio */
AVRational dar = {0, 0}; /* display aspect ratio */
AVRational rate = {0, 0}; /* average fps */
float rate = 0.0; /* average fps */
AVRational rational_rate = {0, 0};

if (v_stream_exists) {
AVStream *v_stream = format_ctx->streams[v_stream_idx];
Expand All @@ -90,9 +96,9 @@ get_av_info(PyObject *self, PyObject *arg) {
int64_t c_duration = av_rescale_q(format_ctx->duration, av_tb, ms_tb);

duration = v_duration;
if (v_duration == 0) {
/* fallback to the container duration if the video stream doesnt
* report anything */
if (duration < 0 || almost_equal(v_duration, 0)) {
/* fallback to the container duration if the video stream
* doesnt report anything */
duration = c_duration;
}

Expand All @@ -101,10 +107,11 @@ get_av_info(PyObject *self, PyObject *arg) {
w = v_codec_ctx->width;
h = v_codec_ctx->height;

rate = format_ctx->streams[v_stream_idx]->avg_frame_rate;
rational_rate = format_ctx->streams[v_stream_idx]->avg_frame_rate;
rate = rational_rate.num * 1.0 / rational_rate.den;

/* calculate num of frames ourselves */
frames = (rate.num * 1.0 / rate.den) * (duration / 1000.0);
frames = rate * duration / 1000.0;

/* if sample aspect ratio is unknown assume it is 1:1 */
sar = format_ctx->streams[v_stream_idx]->sample_aspect_ratio;
Expand Down Expand Up @@ -145,8 +152,9 @@ get_av_info(PyObject *self, PyObject *arg) {
py_safe_set(py_info, "dar_n", PyInt_FromLong(dar.num));
py_safe_set(py_info, "dar_d", PyInt_FromLong(dar.den));
py_safe_set(py_info, "duration", PyFloat_FromDouble(duration));
py_safe_set(py_info, "rate_n", PyInt_FromLong(rate.num));
py_safe_set(py_info, "rate_d", PyInt_FromLong(rate.den));
py_safe_set(py_info, "rate_n", PyInt_FromLong(rational_rate.num));
py_safe_set(py_info, "rate_d", PyInt_FromLong(rational_rate.den));
py_safe_set(py_info, "rate", PyFloat_FromDouble(rate));
py_safe_set(py_info, "frames", PyLong_FromUnsignedLong(frames));

return py_info;
Expand All @@ -156,7 +164,7 @@ static PyObject*
print_av_info(PyObject *self, PyObject *arg) {
PyObject *py_info = get_av_info(self, arg);

if (py_info == NULL) { /* something bad happened */
if (py_info == NULL) {
return (PyObject*)NULL;
}

Expand Down Expand Up @@ -194,11 +202,6 @@ print_av_info(PyObject *self, PyObject *arg) {
x /= 60;
hrs = x % 24;

/* calculate rate */
int rate_n = PyInt_AsLong(PyDict_GetItemString(py_info, "rate_n"));
int rate_d = PyInt_AsLong(PyDict_GetItemString(py_info, "rate_d"));
float rate = rate_n * 1.0 / rate_d;

printf("Video information:");
printf("\n Streams available \t: %s"
"\n Resolution \t: %dx%d, SAR %d:%d DAR %d:%d"
Expand All @@ -212,7 +215,7 @@ print_av_info(PyObject *self, PyObject *arg) {
(int)PyInt_AsLong(PyDict_GetItemString(py_info, "sar_d")),
(int)PyInt_AsLong(PyDict_GetItemString(py_info, "dar_n")),
(int)PyInt_AsLong(PyDict_GetItemString(py_info, "dar_d")),
rate,
(float)PyFloat_AsDouble(PyDict_GetItemString(py_info, "rate")),
hrs, mins, secs,
duration / 1000.0,
PyInt_AsUnsignedLongMask(PyDict_GetItemString(py_info, "frames")));
Expand Down

0 comments on commit 8c9abe2

Please sign in to comment.