/* Copyright (C) 2008 John Hobbs - john@velvetcache.org This file 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 3 of the License, or (at your option) any later version. This file 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 file. If not, see . */ /* libcvfx 1.0.1 A01 Implementation of libcvfx effects. Author: John Hobbs Homepage: http://www.velvetcache.org/ */ #include "cvfx.h" #include namespace cvfx { // Definitions... rgb WHITE = {255,255,255}; rgb BLACK = {0,0,0}; // Init Area // shared int h,i,j,k; CvScalar bgrNonPerm[10]; // brokenTelevision int brokenTelevision_scanlines = 0; // monochrome CvScalar monochrome_bgr; // pixelize CvScalar pixelize_bgr; // memory IplImage * memory_frames[3]; bool memory_init = false; // hFlip IplImage * hFlip_frame; bool hFlip_init = false; // vFlip IplImage * vFlip_frame; bool vFlip_init = false; // pixelLapse IplImage * pixelLapse_frame; bool pixelLapse_init = false; int pixelLapse_x, pixelLapse_y; // quantum IplImage * quantum_frames[8]; bool quantum_init = false; short quantum_counter; // dice IplImage * dice_frame; bool dice_init = false; // filmstrip IplImage * filmstrip_frame; bool filmstrip_init = false; // delayMirror IplImage * delayMirror_frames[24]; int delayMirror_counter; bool delayMirror_init = false; // jitter IplImage * jitter_frame; bool jitter_init = false; // colorStreak IplImage * colorStreak_frames[4]; int colorStreak_counter; bool colorStreak_init = false; // unnamed1 IplImage * unnamed1_frame; int unnamed1_interval = 0; bool unnamed1_init = false; // randomShift IplImage * randomShift_frame; bool randomShift_init = false; // test IplImage * test_frame; int test_interval = 0; bool test_init = false; static short aSin[512]; static float reflectionmap[256][256]; ///////////////////////////////////////////////////////////////// // The Effects /*! Mirrors the left side onto the right side. \param frame The frame to work on. \author John Hobbs john@velvetcache.org */ void mirror (IplImage * frame) { for(i = 0; i < frame->height; i++) { for(j = 0; j < frame->width/2; j++) { bgrNonPerm[0] = cvGet2D(frame,i,j); cvSet2D(frame,i,frame->width-j-1,bgrNonPerm[0]); } } } /*! Vertical mirror. Mirrors top onto bottom. \param frame The frame to work on. \author John Hobbs john@velvetcache.org */ void vmirror (IplImage * frame) { for(int i = 0; i < frame->height/2; i++) { for(int j =0; j < frame->width; j++) { bgrNonPerm[0] = cvGet2D(frame, i, j); cvSet2D(frame,frame->height-i-1,j,bgrNonPerm[0]); } } } /*! Convinience function for a kaleidescope type center mirror. \param frame The frame to work on. \author John Hobbs john@velvetcache.org */ void cmirror (IplImage * frame) { for(int i = 0; i < frame->height/2; i++) { for(int j =0; j < frame->width/2; j++) { bgrNonPerm[0] = cvGet2D(frame, i, j); // Top right cvSet2D(frame,i,frame->width-j-1,bgrNonPerm[0]); // Bottom left cvSet2D(frame,frame->height-i-1,j,bgrNonPerm[0]); // Bottom right cvSet2D(frame,frame->height-i-1,frame->width-j-1,bgrNonPerm[0]); } } } /*! Flips the image horizontally. \param frame The frame to work on. \author John Hobbs john@velvetcache.org */ void hFlip (IplImage * frame) { if(!hFlip_init) { hFlip_frame = cvCreateImage( cvGetSize(frame), frame->depth, 3 ); hFlip_init = true; } hFlip_frame = cvCloneImage(frame); for(int i = 0; i < frame->height; i++) { for(int j = 0; j < frame->width; j++) { bgrNonPerm[0] = cvGet2D(hFlip_frame,i,(frame->width - j - 1)); cvSet2D(frame,i,j,bgrNonPerm[0]); } } } /*! Flips the image vertically. \param frame The frame to work on. \author John Hobbs john@velvetcache.org */ void vFlip (IplImage * frame) { if(!vFlip_init) { vFlip_frame = cvCreateImage( cvGetSize(frame), frame->depth, 3 ); vFlip_init = true; } vFlip_frame = cvCloneImage(frame); for(int i = 0; i < frame->height; i++) { for(int j = 0; j < frame->width; j++) { bgrNonPerm[0] = cvGet2D(vFlip_frame,(frame->height - i - 1),j); cvSet2D(frame,i,j,bgrNonPerm[0]); } } } /*! Reduces the image to the selected color. Can get very dark. \param frame The frame to work on. \author John Hobbs john@velvetcache.org */ void channelSelect (IplImage * frame, channel toKeep) { for(i = 0; i < frame->height; i++) { for(j = 0; j < frame->width; j++) { bgrNonPerm[0] = cvGet2D(frame,i,j); if(toKeep == RED || toKeep == GREEN || toKeep == YELLOW) bgrNonPerm[0].val[0] = 0; if(toKeep == BLUE || toKeep == RED) bgrNonPerm[0].val[1] = 0; if(toKeep == GREEN || toKeep == BLUE) bgrNonPerm[0].val[2] = 0; cvSet2D(frame,i,j,bgrNonPerm[0]); } } } /*! Makes the image monochrome. \param frame The frame to work on. \author John Hobbs john@velvetcache.org */ void monochrome (IplImage * frame) { for (i = 0; i < frame->height; i++){ for (j = 0; j < frame->width; j++){ monochrome_bgr = cvGet2D(frame, i, j); monochrome_bgr.val[0] = monochrome_bgr.val[0]*0.114 + monochrome_bgr.val[1]*0.587 + monochrome_bgr.val[2]*0.299; monochrome_bgr.val[1] = monochrome_bgr.val[0]; monochrome_bgr.val[2] = monochrome_bgr.val[0]; cvSet2D(frame,i,j,monochrome_bgr); } } } /*! Swaps the selected corners. \param frame The frame to work on. \param type The corners to work on, an enum of type "cornersType". \author John Hobbs john@velvetcache.org */ void corners (IplImage * frame, cornersType type) { for(int i = 0; i < frame->height/2; i++) { for(int j = 0; j < frame->width/2; j++) { if(type == TOPLEFT_BOTTOMRIGHT || type == BOTH) { bgrNonPerm[0] = cvGet2D(frame,i,j); bgrNonPerm[1] = cvGet2D(frame,i+(frame->height/2),j+(frame->width/2)); cvSet2D(frame,i,j,bgrNonPerm[1]); cvSet2D(frame,i+(frame->height/2),j+(frame->width/2),bgrNonPerm[0]); } if(type == TOPRIGHT_BOTTOMLEFT || type == BOTH) { bgrNonPerm[0] = cvGet2D(frame,i,j+(frame->width/2)); bgrNonPerm[1] = cvGet2D(frame,i+(frame->height/2),j); cvSet2D(frame,i,j+(frame->width/2),bgrNonPerm[1]); cvSet2D(frame,i+(frame->height/2),j,bgrNonPerm[0]); } } } } /*! Adds simulated 'interlace' lines. \param frame The frame to work on. \author John Hobbs john@velvetcache.org */ void interlaceLines (IplImage * frame) { for(int i = 0; i < 3; i++) bgrNonPerm[0].val[i] = 0; for(int i = 1; i < frame->height; i += 2) for(int j = 0; j < frame->width; j++) cvSet2D(frame,i,j,bgrNonPerm[0]); } /*! Pixelizes the image, makes it nice and chunky. \param frame The frame to work on. \param HOWMANYPIXELS How much pixelization? Any number works, but too big will react unpredictably. \author John Hobbs john@velvetcache.org */ void pixelize (IplImage * frame, int HOWMANYPIXELS) { pixelize_bgr = cvGet2D(frame,0,0); for(int i = 0; i < frame->height; i++) { for(int j = 0; j < frame->width; j++) { bgrNonPerm[0] = cvGet2D(frame,i,j); scalarAverage(pixelize_bgr,bgrNonPerm[0]); if(i%HOWMANYPIXELS == HOWMANYPIXELS-1 && j%HOWMANYPIXELS == HOWMANYPIXELS-1) { for(int r = 0; r < HOWMANYPIXELS; r++) { for(int t = 0; t < HOWMANYPIXELS; t++) { cvSet2D(frame,i-r,j-t,pixelize_bgr); } } pixelize_bgr = bgrNonPerm[0]; } } } } /*! Keeps a few previous frames in memory, and combines them to make ghost images on the current frame. \param frame The frame to work on. \author John Hobbs john@velvetcache.org */ void memory (IplImage * frame, int randomness) { if(!memory_init) { for(int i = 0; i < 3; i++) { memory_frames[i] = cvCreateImage( cvGetSize(frame), frame->depth, 3 ); memory_frames[i] = cvCloneImage(frame); } memory_init = true; } if(0 == getRand(0,randomness)) { memory_frames[getRand(0,2)] = cvCloneImage(frame); } for(int i = 0; i < frame->height; i++) { for(int j =0; j < frame->width; j++) { bgrNonPerm[0] = cvGet2D(frame, i, j); bgrNonPerm[1] = bgrNonPerm[0]; // Used to add weight to the current image for(int k = 0; k < 3; k++) { bgrNonPerm[2] = cvGet2D(memory_frames[k], i, j); scalarAverage(bgrNonPerm[0],bgrNonPerm[2]); } scalarAverage(bgrNonPerm[0],bgrNonPerm[1]); cvSet2D(frame,i,j,bgrNonPerm[0]); } } } /*! Inverts the coloring of the image. \param frame The frame to work on. \author John Hobbs john@velvetcache.org \note Idea from http://tommy.chheng.com/development/windows_development_setup.html, reimplemented using CvScalar. */ void invert (IplImage * frame) { for(int i = 0; i < frame->height; i++) { for(int j =0; j < frame->width; j++) { bgrNonPerm[0] = cvGet2D(frame, i, j); bgrNonPerm[0].val[0] = 255 - bgrNonPerm[0].val[0]; bgrNonPerm[0].val[1] = 255 - bgrNonPerm[0].val[1]; bgrNonPerm[0].val[2] = 255 - bgrNonPerm[0].val[2]; cvSet2D(frame,i,j,bgrNonPerm[0]); } } } /*! Creates a kind of jagged look by stretching pixels across greater horizontal spans. \param frame The frame to work on. \param jaggyness The number of pixels to stretch across. \author John Hobbs john@velvetcache.org */ void hjaggy (IplImage * frame, int jaggyness) { for(int i = 0; i < frame->height; i++) { for(int j =0; j < frame->width; j+= jaggyness) { bgrNonPerm[0] = cvGet2D(frame, i, j); for(int k = 0; k < jaggyness && (j+k) < frame->width; k++) cvSet2D(frame,i,j+k,bgrNonPerm[0]); } } IplImage * frameCopy = cvCreateImage( cvGetSize(frame), frame->depth, 3 ); frameCopy = cvCloneImage(frame); cvSmooth(frameCopy,frame,7); } /*! Cuts image into vertical sections, every other section is vertically flipped. \param frame The frame to work on. \param strips The number of strips to create. \author John Hobbs john@velvetcache.org */ void vStripFlip (IplImage * frame, int strips) { int stripWidth = frame->width/strips; for(int i = 0; i < frame->height/2; i++) { for(int k = 1; k < strips; k+=2) { for(int j = stripWidth*k; j < stripWidth*(k+1); j++) { bgrNonPerm[0] = cvGet2D(frame,i,j); bgrNonPerm[1] = cvGet2D(frame,frame->height-i-1,j); cvSet2D(frame,i,j,bgrNonPerm[1]); cvSet2D(frame,frame->height-i-1,j,bgrNonPerm[0]); } } } } /*! Cuts image into horizontal sections, every other section is horizontally flipped. \param frame The frame to work on. \param strips The number of strips to create. \author John Hobbs john@velvetcache.org */ void hStripFlip (IplImage * frame, int strips) { int stripHeight = frame->height/strips; for(int k = 1; k < strips; k += 2) { for(int i = k*stripHeight; i < (k+1)*stripHeight; i++) { for(int j = 0; j < frame->width/2; j++) { bgrNonPerm[0] = cvGet2D(frame,i,j); bgrNonPerm[1] = cvGet2D(frame,i,frame->width-1-j); cvSet2D(frame,i,j,bgrNonPerm[1]); cvSet2D(frame,i,frame->width-1-j,bgrNonPerm[0]); } } } } /*! Distills the image down to two colors based on luminosity. Replaces over-threshold values with one color, and unser threshold with another. \todo Version that auto adjusts threshold. \param frame The frame to work on. \param threshold The threshold, defaults to 10, but this needs to be carefully measured. \param overThreshold An rgb structure for the color over the threshold. \param underThreshold An rgb structure for the color under the threshold. \author John Hobbs john@velvetcache.org */ void photoCopy (IplImage * frame, rgb overThreshold, rgb underThreshold, int threshold) { threshold = static_cast(255.0*(threshold/100.0)); for(int i = 0; i < frame->height; i++) { for(int j = 0; j < frame->width; j++) { bgrNonPerm[0] = cvGet2D(frame,i,j); if(threshold <= ((bgrNonPerm[0].val[0] + bgrNonPerm[0].val[1] + bgrNonPerm[0].val[2]) / 3)) { bgrNonPerm[0].val[0] = overThreshold.blue; bgrNonPerm[0].val[1] = overThreshold.green; bgrNonPerm[0].val[2] = overThreshold.red; } else { bgrNonPerm[0].val[0] = underThreshold.blue; bgrNonPerm[0].val[1] = underThreshold.green; bgrNonPerm[0].val[2] = underThreshold.red; } cvSet2D(frame,i,j,bgrNonPerm[0]); } } } /*! Distills the image down to, currently, 8 colors. Does so by splitting each channel to 0 or 255, based on a threshold. Much like "photoCopy", but measures and adjusts each channel independently. \param frame The frame to work on. \param threshold The threshold to scale the channel on. Defaults to 100 (max 255, min 0) \author John Hobbs john@velvetcache.org */ void index (IplImage * frame, int threshold) { for(int i = 0; i < frame->height; i++) { for(int j = 0; j < frame->width; j++) { bgrNonPerm[0] = cvGet2D(frame,i,j); if(bgrNonPerm[0].val[0] < threshold) bgrNonPerm[0].val[0] = 0; else bgrNonPerm[0].val[0] = 255; if(bgrNonPerm[0].val[1] < threshold) bgrNonPerm[0].val[1] = 0; else bgrNonPerm[0].val[1] = 255; if(bgrNonPerm[0].val[2] < threshold) bgrNonPerm[0].val[2] = 0; else bgrNonPerm[0].val[2] = 255; cvSet2D(frame,i,j,bgrNonPerm[0]); } } } /*! Loops the image sort of like a busted television. \param frame The frame to work on. \param speed The speed with which to move, from 1 to frame height. Defaults to 45. \author John Hobbs john@velvetcache.org \note Idea from effectv. http://effectv.sourceforge.net/ */ void brokenTelevision (IplImage * frame, int speed) { CvScalar white = cvGet2D(frame,1,1); white.val[0] = 255; white.val[1] = 255; white.val[2] = 255; IplImage * frameCopy = cvCreateImage( cvGetSize(frame), frame->depth, 3 ); frameCopy = cvCloneImage(frame); brokenTelevision_scanlines += speed; for(int i = 0; i < frame->height; i++) { int offset = (brokenTelevision_scanlines+i)%frame->height; for(int j = 0; j < frame->width; j++) { bgrNonPerm[0] = cvGet2D(frameCopy,i,j); cvSet2D(frame,offset,j,bgrNonPerm[0]); } } } /*! BROKEN Supposed to inject some random noise, but doesn't do a very good job of it. \param frame The frame to work on. \param ratio Supposed to be the signal to noise ratio. Isn't. \author John Hobbs john@velvetcache.org */ void noise (IplImage * frame, int ratio) { for(int i = 0; i < frame->height; i++) { for(int j = 0; j < frame->width; j++) { if(getRand(0,1000) < ratio) { bgrNonPerm[0] = cvGet2D(frame,i,j); bgrNonPerm[1] = cvGet2D(frame,i,j); bgrNonPerm[0].val[0] = getRand(0,255); bgrNonPerm[0].val[1] = getRand(0,255); bgrNonPerm[0].val[2] = getRand(0,255); scalarAverage(bgrNonPerm[0],bgrNonPerm[1]); cvSet2D(frame,i,j,bgrNonPerm[0]); } } } } /*! BROKEN Meant for green screen style compositing. \todo Color threshold as an input. \todo Intensity reflected onto the replacement pixels. \param frame The frame to work on. \param replace The frame containing anything to be replaced. \author John Hobbs john@velvetcache.org */ void composite (IplImage * frame, IplImage * replace) { for(int i = 0; i < frame->height; i++) { for(int j = 0; j < frame->width; j++) { bgrNonPerm[0] = cvGet2D(frame,i,j); if(bgrNonPerm[0].val[1] > bgrNonPerm[0].val[0] && bgrNonPerm[0].val[1] > bgrNonPerm[0].val[2]) { if(bgrNonPerm[0].val[0] <= 85 && bgrNonPerm[0].val[2] <= 85) { cvSet2D(frame,i,j,cvGet2D(replace,i,j)); } } } } } /*! Cool time lapse style capture. This can be a very slow effect. If you use the random flag it will places the blocks randomly, and be even slower. \param frame The frame to work on. \param blockSize The size of each replacement block. The smaller the blocks the slower the render. Defaults to 32. \param random If true, then it doesn't go linear. It places random blocks on the screen. Can take a long time to get a full frame. Defaults to false. \author John Hobbs john@velvetcache.org \note Idea from http://www.pixel-lapse.com/ */ void pixelLapse (IplImage * frame, int blockSize, bool random) { if(!pixelLapse_init) { bgrNonPerm[0].val[0] = 0; bgrNonPerm[0].val[1] = 0; bgrNonPerm[0].val[2] = 0; pixelLapse_frame = cvCreateImage(cvGetSize(frame), frame->depth, 3); for(int i = 0; i < frame->height; i++) for(int j = 0; j < frame->width; j++) cvSet2D(pixelLapse_frame,i,j,bgrNonPerm[0]); pixelLapse_init = true; pixelLapse_x = 0; pixelLapse_y = 0; } for(int i = 0; i < blockSize && (i+pixelLapse_y) < frame->height; i++) { for(int j = 0; j < blockSize && (j+pixelLapse_x) < frame->width; j++) { cvSet2D(pixelLapse_frame,i+pixelLapse_y,j+pixelLapse_x,cvGet2D(frame,i+pixelLapse_y,j+pixelLapse_x)); } } if(random) { pixelLapse_x = getRand(0,frame->width-1); pixelLapse_y = getRand(0,frame->height-1); } else { pixelLapse_x += blockSize; if(pixelLapse_x >= frame->width) { pixelLapse_x = 0; pixelLapse_y += blockSize; if(pixelLapse_y >= frame->height) pixelLapse_y = 0; } } //! \todo Is there a better way here? for(int i = 0; i < frame->height; i++) for(int j = 0; j < frame->width; j++) cvSet2D(frame,i,j,cvGet2D(pixelLapse_frame,i,j)); } /*! Does a fuzzy thing with movement. \param frame The frame to work on. \author John Hobbs john@velvetcache.org \note Idea and pseudo code from effectv, 'quark'. http://effectv.sourceforge.net/ */ void quantum (IplImage * frame) { if(!quantum_init) { for(int i = 0; i < 8; i++) { quantum_frames[i] = cvCreateImage(cvGetSize(frame), frame->depth, 3); quantum_frames[i] = cvCloneImage(frame); } quantum_counter = 0; quantum_init = true; } quantum_frames[quantum_counter] = cvCloneImage(frame); for(int i = 0; i < frame->height; i++) for(int j = 0; j < frame->width; j++) cvSet2D(frame,i,j,cvGet2D(quantum_frames[getRand(0,7)],i,j)); if(++quantum_counter > 7) quantum_counter = 0; } /*! BROKEN Cuts the image into blocks, the rotates them randomly. \param frame The frame to work on. \param blockSize The size of the blocks. \author John Hobbs john@velvetcache.org \note Idea from effectv, 'diceTV'. http://effectv.sourceforge.net/ */ void dice (IplImage * frame, int blockSize) { return; if(!dice_init) { dice_frame = cvCreateImage(cvGetSize(frame), frame->depth, 3); dice_init = true; } dice_frame = cvCloneImage(frame); for(int i = 0; i < frame->height; i += blockSize) { for(int j = 0; j < frame->width; j += blockSize) { int flip = getRand(1,3); // 0 - no, 1 - 90o, 2 - 180, 3 - 240 if(2 == flip) { for(int k = 0; k < blockSize && k+i < frame->height; k++) { for(int l = 0; l < blockSize && l+j < frame->width; l++) { cvSet2D(frame,l+i,k+j,cvGet2D(dice_frame,k+i,l+j)); } } } else if(1 == flip) { /*for(int k = 0; k < blockSize && k+i < frame->height; k++) { for(int l = 0; l < blockSize && l+j < frame->width; l++) { cvSet2D(frame,(i+blockSize)-k,(j+blockSize)-l,cvGet2D(dice_frame,k+i,l+j)); } }*/ } else if(3 == flip) { for(int k = 0; k < blockSize && k+i < frame->height; k++) { for(int l = 0; l < blockSize && l+j < frame->width; l++) { cvSet2D(frame,(i+blockSize)-k,j+l,cvGet2D(dice_frame,k+i,l+j)); } } } } } } /*! Like a normal horizontal mirror, but one side is out of sync. \todo No actual mirroring right now, just out of sync. \param frame The frame to work on. \author John Hobbs john@velvetcache.org */ void delayMirror (IplImage * frame) { if(!delayMirror_init) { for(int i = 0; i < 24; i++) { delayMirror_frames[i] = cvCreateImage(cvGetSize(frame), frame->depth, 3); delayMirror_frames[i] = cvCloneImage(frame); } delayMirror_init = true; delayMirror_counter = 0; } if(++delayMirror_counter >= 24) delayMirror_counter = 0; delayMirror_frames[delayMirror_counter] = cvCloneImage(frame); for(int i = 0; i < frame->height; i++) { for(int j = frame->width/2; j < frame->width; j++) { cvSet2D(frame,i,j,cvGet2D(delayMirror_frames[(delayMirror_counter+12)%24],i,j)); } } } /*! Like a normal horizontal mirror, but one side is out of sync. \todo No actual mirroring right now, just out of sync. \param frame The frame to work on. \author John Hobbs john@velvetcache.org \note Idea from effectv. http://effectv.sourceforge.net/ */ void jitter (IplImage * frame) { if(!jitter_init) { jitter_frame = cvCreateImage(cvGetSize(frame), frame->depth, 3); jitter_frame = cvCloneImage(frame); jitter_init = true; } if(1 == getRand(0,3)) { jitter_frame = cvCloneImage(frame); } else if(1 == getRand(0,3)) { //! \todo How come this doesn't work? //frame = cvCloneImage(jitter_frame); for(int i = 0; i < frame->height; i++) for(int j = 0; j < frame->width; j++) cvSet2D(frame,i,j,cvGet2D(jitter_frame,i,j)); } } /*! BROKEN Do streaky memory but tint each frame a different color \todo This is slooooow. Consider only changing "very" different pixels and doing color inline. \param frame The frame to work on. \author John Hobbs john@velvetcache.org \note Idea from effectv. http://effectv.sourceforge.net/ */ void colorStreak (IplImage * frame) { if(!colorStreak_init) { for(int i = 0; i < 4; i++) { colorStreak_frames[i] = cvCreateImage(cvGetSize(frame), frame->depth, 3); colorStreak_frames[i] = cvCloneImage(frame); } colorStreak_init = true; colorStreak_counter = 0; } if(++colorStreak_counter >= 4) colorStreak_counter = 0; colorStreak_frames[colorStreak_counter] = cvCloneImage(frame); if(0 == colorStreak_counter) channelSelect(colorStreak_frames[colorStreak_counter],RED); else if(1 == colorStreak_counter) channelSelect(colorStreak_frames[colorStreak_counter],GREEN); else if(2 == colorStreak_counter) channelSelect(colorStreak_frames[colorStreak_counter],BLUE); else channelSelect(colorStreak_frames[colorStreak_counter],YELLOW); for(int i = 0; i < frame->height; i++) { for(int j =0; j < frame->width; j++) { bgrNonPerm[0] = cvGet2D(frame, i, j); bgrNonPerm[1] = bgrNonPerm[0]; // Used to add weight to the current image for(int k = 0; k < 4; k++) { bgrNonPerm[2] = cvGet2D(colorStreak_frames[k], i, j); scalarAverage(bgrNonPerm[0],bgrNonPerm[2]); } scalarAverage(bgrNonPerm[0],bgrNonPerm[1]); cvSet2D(frame,i,j,bgrNonPerm[0]); } } } /*! As object move farther from the left of the frame, their 'ghost' image separates farther from the object. \todo Find a witty name. \param frame The frame to work on. \author John Hobbs john@velvetcache.org */ void unnamed1 (IplImage * frame) { if(!unnamed1_init) { unnamed1_frame = cvCreateImage(cvGetSize(frame), frame->depth, 3); unnamed1_init = true; } unnamed1_frame = cvCloneImage(frame); for(int i = 0; i < frame->height; i++) { unnamed1_interval = 0; for(int j = 0; j < frame->width; j++) { bgrNonPerm[0] = cvGet2D(unnamed1_frame,i,j); scalarAverage(bgrNonPerm[0],cvGet2D(unnamed1_frame,i,j-(unnamed1_interval/2))); cvSet2D(frame,i,j,bgrNonPerm[0]); unnamed1_interval++; } } } /*! Randomly shift rows by random amounts. \param frame The frame to work on. \param chance If you don't want 100% of the frames shifted, change this. \param mshift The maximum shift value. Values over the frame width are reduced automatically. \author John Hobbs john@velvetcache.org */ void randomShift (IplImage * frame, int chance, int mshift) { if(!randomShift_init) { randomShift_frame = cvCreateImage(cvGetSize(frame), frame->depth, 3); randomShift_init = true; } randomShift_frame = cvCloneImage(frame); mshift = mshift%frame->width; int shift = 0; for(int i = 0; i < frame->height; i++) { if(0 == getRand(0,chance)) { shift = getRand(0,mshift); for(int j = 0; j < frame->width; j++) { cvSet2D(frame,i,j,cvGet2D(randomShift_frame,i,(j+shift)%frame->width)); } } } } // Look into cvFilter2D void test (IplImage * frame) { } // Internal Stuff /*! Convinience function to average two CvScalars. \param left The CvScalar to put the result in. \param right The other CvScalar to average with. \author John Hobbs john@velvetcache.org */ void scalarAverage (CvScalar & left, const CvScalar & right) { left.val[0] = (right.val[0]+left.val[0])/2; left.val[1] = (right.val[1]+left.val[1])/2; left.val[2] = (right.val[2]+left.val[2])/2; } /*! Convinience function to get random integer within a set of bounds. \param lowerBound The lower bound. \param upperBound The upper bound. \author John Hobbs john@velvetcache.org */ int getRand (int lowerBound, int upperBound) { if(lowerBound == upperBound) return lowerBound; return lrand48()%upperBound+lowerBound; } /*! BROKEN Return the average "weight" of the image. \todo This is a flawed algorithm. The method of averaging is bad. ((5+7/2)+8)/2 != (5+7+8)/3 \param frame The frame to measure. \return The luminosity from 0 - 255 */ int getFrameLuminosity (IplImage * frame) { bgrNonPerm[0] = cvGet2D(frame, 1, 1); for(int i = 0; i < frame->height; i++) { for(int j =0; j < frame->width; j++) { bgrNonPerm[1] = cvGet2D(frame, i, j); scalarAverage(bgrNonPerm[0],bgrNonPerm[1]); } } return static_cast((bgrNonPerm[0].val[0] + bgrNonPerm[0].val[1] + bgrNonPerm[0].val[2])/3); } }