Transform: SLIT-SCAN
Slitscanning is a process for transforming the frames of a video into a single image. Slitscans can produce a variety of interesting visual effects and, depending on the source, can often reveal interesting patterns in the source video.
This example works with a movie file or webcam to supply a continuous feed of images. For each image read from the movie file or camera, only one row of pixels is used (the rest are ignored). New slices are added to the right, while older slices shift to the left and are automatically deleted when they reach the edge of the screen.
/**
* Transform: Slit-Scan
* from Form+Code in Design, Art, and Architecture
* by Casey Reas, Chandler McWilliams, and LUST
* Princeton Architectural Press, 2010
* ISBN 9781568989372
*
* This code was written for Processing 1.2+
* Get Processing at http://www.processing.org/download
*/
import processing.video.*;
Movie myVideo;
int video_width = 768;
int video_height = 576;
int video_slice_x = video_width/2;
int window_width = 1000;
int window_height = video_height;
int draw_position_x = 0;
boolean newFrame = false;
void setup() {
myVideo = new Movie(this, "input.mov");
size(window_width, window_height, P2D);
background(0);
myVideo.loop();
}
void movieEvent(Movie myMovie) {
myMovie.read();
newFrame = true;
}
void draw() {
if (newFrame) {
loadPixels();
for (int y=0; y<window_height; y++){
int setPixelIndex = y*window_width + draw_position_x;
int getPixelIndex = y*video_width + video_slice_x;
pixels[setPixelIndex] = myVideo.pixels[getPixelIndex];
}
updatePixels();
draw_position_x++;
if (draw_position_x >= window_width) {
exit();
}
newFrame = false;
}
}
Contributed Examples
-
OpenFrameworks
/** * Transform: SlitScan from Form+Code in Design, Art, and Architecture * implemented in OpenFrameworks by Anthony Stellato <http://rabbitattack.com/> * * Requires OpenFrameworks available at http://openframeworks.cc/ * * For more information about Form+Code visit http://formandcode.com */ // =========================================================== // - Transform_SlitScan.h // =========================================================== #include "ofMain.h" class testApp : public ofBaseApp{ public: void setup(); void update(); void draw(); void setPixel(int horizontal,int vertical,unsigned char R, unsigned char G,unsigned char B,int w, unsigned char pixels[]); void getPixel(int horizontal,int vertical,unsigned char* R, unsigned char* G,unsigned char* B,int w, unsigned char pixels[]); ofVideoPlayer player; ofTexture texture; unsigned char * texpixels, * vidpixels; }; // =========================================================== // - Transform_SlitScan.cpp // =========================================================== #include "Transform_SlitScan.h" int video_width = 160; int video_height = 120; int video_slice_x = video_width/2; int window_width = 1000; int window_height = video_height; int draw_position_x = 0; bool newFrame = false; //-------------------------------------------------------------- void testApp::setup(){ player.loadMovie("station.mov"); player.setLoopState(1); player.play(); texture.allocate(window_width, window_height, GL_RGB); texpixels = new unsigned char [window_width * window_height * 3]; } //-------------------------------------------------------------- void testApp::update(){ player.idleMovie(); newFrame = player.isFrameNew(); if(newFrame){ vidpixels = player.getPixels(); for (int y=0; y<window_height; y++){ unsigned char r, g, b; getPixel(video_slice_x, y, &r, &g, &b, video_width, vidpixels); setPixel(draw_position_x, y, r, g, b, window_width, texpixels); } texture.loadData(texpixels, window_width, window_height, GL_RGB); draw_position_x++; if (draw_position_x >= window_width) { OF_EXIT_APP(0); } newFrame = false; } } //-------------------------------------------------------------- void testApp::draw(){ ofBackground(0, 0, 0); texture.draw(0, 0); } // The following code is from the Pixel by Pixel Openframeworks Course // and is very useful if I do say myself. //-------------------------------------------------------------- void testApp::setPixel(int horizontal,int vertical,unsigned char R,unsigned char G, unsigned char B,int w, unsigned char pixels[]){ int thisPixel; thisPixel = 3*(w * vertical +horizontal); pixels[thisPixel]=R; pixels[thisPixel+1]=G; pixels[thisPixel+2]=B; } //-------------------------------------------------------------- void testApp::getPixel(int horizontal,int vertical,unsigned char* R,unsigned char* G, unsigned char* B,int w, unsigned char pixels[]){ int thisPixel; thisPixel = 3*(w * vertical +horizontal); *R= pixels[thisPixel]; *G= pixels[thisPixel+1]; *B= pixels[thisPixel+2]; }
-
Cinder
/** * Transform: Slit-Scan from Form+Code in Art, Design, and Architecture * implemented in C++ by Patrick Tierney <http://ptierney.com> * * A variation of this example by Forrest Oliphant <http://sembiki.com> * which takes advantage of cinder specific optimizations * can be found at https://gist.github.com/856451 * * Requires Cinder 0.8.2 available at http://libcinder.org * * Project files are located at https://github.com/hlp/form-and-code * * For more information about Form+Code visit http://formandcode.com */ #include "cinder/app/AppBasic.h" #include "cinder/Surface.h" #include "cinder/Capture.h" class Transform_SlitScan : public ci::app::AppBasic { public: void prepareSettings(Settings* settings); void setup(); void update(); void draw(); void shutdown(); private: ci::Capture capture; ci::Surface surface; int video_width; int video_height; int video_slice_x; int window_width; int window_height; int draw_position_x; bool newFrame; int data_size; GLubyte* data; }; void Transform_SlitScan::prepareSettings(Settings* settings) { video_width = 320; video_height = 240; video_slice_x = video_width / 2; window_width = 1000; window_height = video_height; draw_position_x = 0; newFrame = false; data_size = window_width * window_height * 3; settings->setWindowSize(window_width, window_height); } void Transform_SlitScan::setup() { try { capture = ci::Capture(video_width, video_height); capture.start(); } catch (...) { // if we threw in the start close the program ci::app::console() << "Unable to open webcam." << std::endl; exit(1); } if (capture.getWidth() != video_width || capture.getHeight() != video_height) { ci::app::console() << "Unable to open webcam at desired size." << std::endl; exit(1); } // create new pixel buffer data = new GLubyte[data_size]; // set all pixels to black for (int i = 0; i < data_size; ++i) { data[i] = (GLubyte) 0; } } void Transform_SlitScan::update() { newFrame = false; if (!capture || !capture.checkNewFrame()) return; newFrame = true; surface = capture.getSurface(); ci::Surface::Iter it = surface.getIter(); int i = window_height - 1; while(it.line()) { int j = 0; while(it.pixel()) { if (j == video_slice_x) { data[i * window_width * 3 + draw_position_x * 3] = (GLubyte) it.r(); data[i * window_width * 3 + draw_position_x * 3 + 1] = (GLubyte) it.g(); data[i * window_width * 3 + draw_position_x * 3 + 2] = (GLubyte) it.b(); } j++; } i--; } } void Transform_SlitScan::draw() { if (!newFrame) return; draw_position_x++; if (draw_position_x >= window_width) draw_position_x = 0; glDrawPixels(window_width, window_height, GL_RGB, GL_UNSIGNED_BYTE, data); } void Transform_SlitScan::shutdown() { delete [] data; } CINDER_APP_BASIC(Transform_SlitScan, ci::app::RendererGl)
We are looking for implementations of the code examples in other programming languages to post on the site. If you would like to submit a sample, or if you find a bug, please write to