Category: digital


Compositing The Geeky Way with ImageMagick & FFmpeg

17. September 2011 - 15:04 Uhr

The Task:
We have several videos which need to be cropped. Plus there needs to be a logo shown in the lower left corner.

The Constraints:
Since the need for video editing / compositing occurs about once in a decade, it’s economically inappropriate to buy editing software for this simple task – and outsourcing the job would have taken too much time.

The Solution:

  1. Installation of FFmpeg and ImageMagick inside an Ubuntu VM (using Parallels under MacOS X).
  2. Looking up the required arguments, switches etc. to chop the existing videofile into a sequence of PNG files using FFmpeg.
  3. Mounting the MacOS X Folder containing the videos via sshfs inside the Ubuntu VM (somehow the penguin still appeals to me).
  4. Scaling and cropping the PNGs to fit the new height and width via ImageMagick.
  5. Composing a PNG containing the logos over each single PNG of the image sequence (via ImageMagick).
  6. Turning the final PNG sequence into an flv using FFmpeg.
  7. Looking at the whole process and realising that I gained several geekness levels ;-)

The Code:

#!/usr/bin/perl
use strict;
use warnings;

# getting the commandline arguments
# (ok, not really userfriendly - what should I say?
# I had to be fast ... ).

my $src_video = $ARGV[0];
my $logo_png = $ARGV[1];
my $src_sequence_dir = $ARGV[2];
my $resized_sequence_dir = $ARGV[3];
my $cropped_sequence_dir = $ARGV[4];
my $composed_sequence_dir = $ARGV[5];
my $dest_video = $ARGV[6];

#splitting up the source video:
`ffmpeg -i $src_video -r 19 -s 704x368 -f image2 \
  $src_sequence_dir/frame_%05d.png`;

# processing the pngs:
while(<$src_sequence_dir/*>){
	my $filename = (split("/",$_))[-1];

	# resize images:
	`convert -resize 689x360 $src_sequence_dir/$filename \
             $resized_sequence_dir/$filename`;	

	# crop images:
	`convert -gravity Center -crop 640x360-0+0 +repage \
             $resized_sequence_dir/$filename \
             $cropped_sequence_dir/$filename`;

	# compose images
	`composite -compose atop -gravity SouthWest \
             $logo_png $resized_sequence_dir/$filename \
             $composed_sequence_dir/$filename`;	

    # yes, this could have been done with more style,
    # but I wanted the results of every step of the
    # imageprocessing in a separate directory.
}

#putting the png sequence together again:
`ffmpeg -f image2 -b 2400kb -r 19 -i \
    $composed_sequence_dir/frame_%05d.png $dest_video`;

# yes, this could have also been done with a shell script ...

Kommentieren » | digital

The Camel Within

11. June 2011 - 18:01 Uhr

The Camel Within

A Bigger Apple

After writing a little program that generates a simple HTML-presentation from a folder of JPG-Files, I’ve been searching for a way to enable my colleagues to use it – without the need to learn how to handle a command line interface.

Making a Perl program executable via doubleclick in the graphical user inteface of Mac OS X is ridiculously easy: just change the suffix to “.command”, set the rights of the file to executable and Mac OS X will do your bidding.

There is, however, a little obstacle in case you to work with relative paths – Mac OS X will run this program with the current working directory set to your home folder. But this can be overcome with a little shell command:

my $basepath = `dirname "$0"`;
chomp($basepath);

(Yes, I am using backticks – sue me! ;-) )

Kommentieren » | digital

Password Mantras

11. June 2011 - 17:09 Uhr

With the capacity of the human memory and patience being limited and with every website, electronic device and service asking for authentication, lots of people are reusing the excact same password for each and every one of their accounts. The implications of this kind of behaviour in terms of security have been discussed ad nauseam, hence I won’t bore you with warnings concerning identity theft, etc.
Nevertheless there is perhaps another effect, which hasn’t been given this much attention (at least I haven’t read or heard about any serious research on this phenomenon yet):

Unless the chosen password is purely random and truly void of meaning, reusing the same word or phrase over and over again may have a psychological effect.

Let’s say someone chose to use 4poCalYps3! as his or her standard password (with the idea of an apocalypse being very memorable, chances are good that this has indeed been used by some kind of half-geek out there). Thus everytime he or she logs into a (password protected) computer or website, the word or rather the idea of an apocalypse shows up in this persons mind.

If this person is digitally very active, he or she actually thinks of the apocalypse several times during a common workday. This could lead to an awkward kind of self-conditioning, perhaps even with a negative effect on that persons mental health.

Perhaps choosing a password embedded within positive semantic context could have favorable effects … ?

4llY0urMoN3yw1llbeM1ne
;-)

3 Kommentare » | digital

Pizza Calculator

23. October 2010 - 13:57 Uhr
Pizza Calculator, (c) 2010 Ingmar Drewing

This problem is as old as mankind: it's about ten o'clock in the evening and you and your friends are about to order some pizza - but is it really cheaper (per cm2) to order the family-sized pizza or would you be better off with several smaller ones?

Do not despair, for the solution is here - just use the pizza calculator:

Diameter: cm
Price:



Resulting price: Cent per cm2


[ "Nerdy"? What do you mean "nerdy"? ;-) ]

Kommentieren » | digital

Digital Sketch

1. July 2010 - 09:59 Uhr
Digital Sketch

Nightly Sketch Nr. 241

After some messing around with the  SimpleParticleSystem by Daniel Schiffman, the resulting applet could be conviced to generate a kind of fur texture.  If you're interested in the tweaked processing source code, you'll find it here.

3 Kommentare » | digital

Nervous Spot

15. June 2010 - 19:40 Uhr
This movie requires Flash Player 9

Playing around with AS3, perhaps somebody wants to take the code and make a proper Rorschach test ;-) :

/*
Copyright (c) 2010 Ingmar Drewing

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:

The above copyright notice and this permission notice shall be 
included in all copies or substantial portions of the Software.

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.
*/

package  {
  import flash.display.*;
  import flash.utils.*;
  import flash.events.Event;
  import flash.events.MouseEvent;
  import flash.filters.BlurFilter;
  import flash.geom.*;

  [SWF(backgroundColor="#FFFFFF",frameRate="60",width="460",height="460")]

  public class Spot extends Sprite {
  private var W:int;
  private var H:int;
  private var rendering:Boolean = false;
  private var dots:Array = new Array();
  private var canvas:Bitmap = new Bitmap();
  private var range:int = 80;
  private var bg_colour:Number = 0xFFFFFF;
  private var centerpoint:Point;
  private var sync_x:Number;
  private var sync_y:Number;
  private var check_interval:int;

  private var colour:Number = 0x000000;

  public function Spot () {
    init();
  }

  private function init():void{
    H = stage.stageHeight;
    W = stage.stageWidth;
    centerpoint = new Point( W/2, H/2 );
    sync_x = W/2;
    sync_y = H/2;

    for ( var dx:int = 1 ; dx < W; dx+=10 ){
      for ( var dy:int = 1 ; dy < H; dy+=20 ){
        var d:Point = new Point( dx, dy );
        dots.push(d);
      }
    }

    addChild( canvas );
    render();
    stage.addEventListener( MouseEvent.MOUSE_MOVE,
    start_following_mouse );
  }

  private function stop_following_mouse( e:Event ):void{
    removeEventListener( Event.ENTER_FRAME, mouse_sync );
    sync_x = W/2;
    sync_y = H/2;
    stage.addEventListener( MouseEvent.MOUSE_MOVE,
        start_following_mouse );
    check_interval = setInterval( check_render_necessity, 500 );
  }

  private function check_render_necessity():void{
    var dx:Number = sync_x - centerpoint.x;
    var dy:Number = sync_y - centerpoint.y;
    var distance:Number = Math.sqrt( dx*dx + dy*dy );
    if ( distance < 1 ){
      stop_rendering();
      clearInterval( check_interval );
    }
  }

  private function start_following_mouse( e:MouseEvent=null ):void{
    clearInterval( check_interval );
    start_rendering();
    stage.removeEventListener( MouseEvent.MOUSE_MOVE,
      start_following_mouse );
    stage.addEventListener( Event.MOUSE_LEAVE, stop_following_mouse );
    addEventListener( Event.ENTER_FRAME, mouse_sync );
  }

  private function mouse_sync( e:Event ):void{
    sync_x = mouseX;
    sync_y = mouseY;
  }

  private function start_rendering():void{
    if ( !rendering ){
      rendering = true ;
      addEventListener( Event.ENTER_FRAME , render );
    }
  }

  private function stop_rendering():void{
    if ( rendering ){
      rendering = false ;
      removeEventListener( Event.ENTER_FRAME , render );
    }
  }

  private function render( e:Event=null ):void{
    centerpoint.x = sync_x - ( sync_x - centerpoint.x )*5/6 ;
    centerpoint.y = sync_y - ( sync_y - centerpoint.y )*5/6  ;
    var bd:BitmapData = new BitmapData( W, H, false, bg_colour );
    bd.lock();

    for each ( var dot:Point in dots ){
      var dx:Number = dot.x - centerpoint.x ;
      var dy:Number = dot.y - centerpoint.y ;
      var distance:Number = Math.sqrt( dx*dx + dy*dy );

      if ( distance < range ){
        var r:int = (1-distance/range) *1000;

        var current_x:int = dot.x;
        var current_y:int = dot.y;
        for( var i:int = 0; i < r; i++){
          bd.setPixel(current_x, current_y, colour);
          var randX:Number =  Math.random();
          if ( randX < .33 ){                             
            current_x -=1;                         
          }                         
          else if ( randX >=.33 && randX < .66 ){
            current_x +=1;
          }
           var randY:Number =  Math.random();
           if ( randY < .33 ){                             
             current_y -=1;                         
          }                         
         else if ( randY >=.33 && randY < .66 ) {
           current_y +=1;
        }
      }
    }
  }

  bd.unlock();
  canvas.bitmapData = bd;
  }
 }
}

1 Kommentar » | digital

Rogue Revisited

5. December 2009 - 22:44 Uhr

Back in the early 1990ies I had an Atari 520 st. I have to admit, that I used it mainly to play games like "Wings of Death" or "Fusion".

Another game I really liked was Rogue, a single player fantasy adventure game. The dungeon was randomly generated for every new game. Though there was a finite number of magical items, potions and scrolls, one never knew which effect a certain item had until it was identified either by a spell or by using it (the latter method had it's drawbacks, since there were also cursed items). This kind of randomness caused a very high degree of replayability.

Somewhere in early 2008 I found out that rogue is older than I assumed. It has been initially written in the early 80ies for Unix machines. The moment I read about this, I started to search for a playable version to use on my desktop computer (I am mainly using ubuntu). And I found it - it's part of the package "bsdgames-nonfree" :)

It's still fun to play and having learned to use vim (a very sophistacated text-editor) I noticed that the ASCII-version of Rogue used the same keys to move the hero around, which move the cursor in vim.

If you haven't seen the game yet, have a look - it's a part of gaming history and influenced a lot of younger games. http://en.wikipedia.org/wiki/Rogue_(computer_game)

Kommentieren » | digital

Building a Preloader with ActionScript 3 using mxmlc and the Frame Pragma

23. September 2009 - 13:56 Uhr

Good news: you really don't need the timeline to build a preloader. The technique is described in detail at bit-101. In short it works like this:

1. put this before the definition of your main class:

[Frame(factoryClass="myPackage.MyPreloadClass")]

The code above tells the mxmlc to take the given (custom) class under myPackage.MyPreloadClass and use it as a Preloader (there is some more background info about the Frame-pragma on the adobe-blog).

2. Write your preloader-class, extending the MovieClip-Class (since you need something with frames and sprites don't have them). See bit-101 for an example-class.

3. That's it. Er ... well, not completely, since if you need to load some other stuff, say for example depending on a CMS-generated XML-File, the path to which is handed to your swf using flashvars, you will no longer get the the flashvars with

LoaderInfo(mainClass.root.loaderInfo).parameters

but with

LoaderInfo(preloader.root.loaderInfo).parameters

where "preloader" is a reference to the preloader-instance - that's why I am passing a reference to the Preloader as a parameter when instantiating my main class.

5. Now your preloader would still think everything is ready, once the swf is completely loaded. That's not the case, since you still lack the stuff defined by your on-the-fly-cms-generated XML.

My solution is to pass my main class a callback-function as another parameter, which tells the preloader when the loading is really done.

In principle the passing of a preloader-reference to the main class would suffice, but then the function triggering the end of the loader-animation would have to be defined public ...

Kommentieren » | digital

Manipulating Bitmap Images with ActionScript 3

8. September 2009 - 19:40 Uhr

I've been recently working on a project which involved the manipulation of bitmap data at runtime. The swf would be communicating with a content management system, which only provided coloured images. But since the images needed to be displayed in greyscale as well as in colour, the swf had to do the trick.

In ActionScript you need to use the ColorMatrixFilter to manipulate these properties. A procedure which can be mind boggling and definitely is error-prone.

After a little searching I found the extremely handy class ColorMatrix.as written by ActionScript grandmaster Grant Skinner. You'll find some examples on his blog and the class itself hosted on google code.

The usage is fairly simple, though you need to take care of the order of the function calls:

import com.gskinner.geom.ColorMatrix;
import flash.filters.ColorMatrixFilter;
import flash.display.Sprite;

/* ... some other code in between ...*/

var c:ColorMatrix = new ColorMatrix();
c.adjustBrightness(100);
c.adjustSaturation(-100);
c.adjustContrast(-50);

var cmf:ColorMatrixFilter = new ColorMatrixFilter();
cmf.matrix = cm;

// The following bitmapData is the BitmapData-Object
/  derived from an imagefile,
// which is loaded at runtime:

var bmp:Bitmap =  new Bitmap(bitmapData) ;

var s:Sprite = new Sprite();
s.addChild( bmp );
s.filters=[cmf];

// that's it.

It's a breeze and works like a charm. Just keep in mind, that these lines:

c.adjustBrightness(100);
c.adjustSaturation(-100);
c.adjustContrast(-50);

will result in a different image compared to the product of these lines:

c.adjustSaturation(-100);
c.adjustContrast(-50);
c.adjustBrightness(100);

Kommentieren » | digital

Praise To A Perl Module

25. August 2009 - 20:15 Uhr

For as long as our design agency owns a fileserver, which is approximately three years, a little program written in Perl eases our daily lives at the office. It makes heavy use of Linux::Inotify2, an immensely useful module.

We have a samba-share on our fileserver, which contains a directory for each of our clients. Whenever anyone in our office creates a new folder inside this directory its name ist automatically checked and a three letter acronym is generated from the clients name. This abbrevation will be used internally to identify files related to the client given.

Once the new folder ist created it itself is also watched by the program and whenever a project folder is created within this clients folder, a directory hierearchy is copied into the project directory which looks similar to this:

00_communication
01_projectmanagement
02_design
03_implementation
04_final

This is very helpful, since nobody has to remember to create a "corporate folder structure", which can also be understood by his or her co-workers. This is crucial if, for example, a client requests data while one of our colleagues is on vacation (and in this business you always need to find such things instantly).

With Perl being the almighty programming language it is, the usefulness of the module doesn't end at here. You can really trigger anything with it. One could create a folder which synchronises its contents with a remote directory on a ftp-server the moment sombody drops data into it.  Or you can have the system email you about an activity regarding certain files, regarding security issues and the like.

The possibilities are endless.

2 Kommentare » | digital

« Ältere Einträge