Skip to content

Instantly share code, notes, and snippets.

@bl0ckeduser
Created July 19, 2012 18:17
Show Gist options
  • Save bl0ckeduser/3145783 to your computer and use it in GitHub Desktop.
Save bl0ckeduser/3145783 to your computer and use it in GitHub Desktop.
ChromakeyMan v2.2
/*
* ChromakeyMan v2.2
* by 'Bl0ckeduser'
*
* A simple chroma-key effect program
*
* Originally written in METAL Basic
* (on Mac OS 9) in 2007-2008 for ad-hoc
* use in a little VFX project.
*
* SDL/C port, July 2012
*
* getpixel() and putpixel() are from
* Sam Lantinga's excellent SDL docs.
*
* Improvements being made July 2012:
* - local RGB tolerance
* - pixel adjacency check --
* avoid keying out regions
* not contiguous with already
* keyed-out regions
* - a little refactoring and commenting,
* but not enough
* - option to turn on/off these new
* features
*
* WARNING: Never written with any serious
* algorithms in mind. The code quality
* is quite low as I wrote the core when
* I was around 13 in Basic. (gotos everywhere)
* I didn't refactor much. I write better
* code today, I assure you. The technical terms
* are probably wrong.
*
* BUGS:
* - Isolated regions (not contiguous with
* edges of frame) may not be properly
* keyed out. Possible solution: new vertical
* passes in addition to the current two
* horizontal ones.
*
* Input: F1.bmp, F2.bmp, F3.bmp, ... in local
* directory
*
* Output: same, if -s option is used.
* Otherwise, none. Keyed out
* region is converted to pure
* green pixels.
*
* You can use ffmpeg tricks to convert
* to and from BMP-reel (see http://pr0gr4mm3r.com/linux/
* convert-video-to-images-and-back-using-ffmpeg/).
* I use:
* ffmpeg -i moviefile -r 30 -f image2 REEL/F%d.bmp
* ffmpeg -i REEL/F%d.bmp -r 30 -vcodec huffyuv reel.avi
*
* Use: launch program. Type in values
* for parameters. Click on a pixel
* part of region to be keyed. Watch
* result. The parameters depend on the
* quality of the video source and its
* properties, etc. Some fiddling required.
*/
#include "SDL.h"
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
void putpixel(SDL_Surface *surface, int x, int y, Uint32 pixel);
Uint32 getpixel(SDL_Surface *surface, int x, int y);
void plot(int x, int y);
/*
* Emulate the QuickDraw-style
* graphics calls in METAL
*/
void forecolor(int r, int g, int b);
void get_pixel(int x, int y, int* r, int* g, int* b);
int forecolor_r,
forecolor_g,
forecolor_b;
SDL_Surface *screen;
int xl, yl;
void fail(char *s) {
fprintf(stderr, "failure: %s\n", s);
exit(1);
}
int main(int argc, char** argv)
{
int dif = 17000; /* POWER : dif */
int areatol;
int lvalid = 0;
int privater, privateg, privateb;
int fid;
int ex, ey;
char buf[1024];
int mx, my;
int r, g, b;
int rmn = 0, rmp = 0;
int gmn = 0, gmp = 0;
int bmn = 0, bmp = 0;
int rcolr, rcolg, rcolb;
int nr, ng, nb;
int off = 0;
/* local tolerance */
int localtol;
int pr, pg, pb;
int chk;
/* pixel adjacency matrix */
int** matrix;
int i, j;
int kc;
int achk;
int ltmode, adjmode;
SDL_Surface *image;
SDL_Event event;
const SDL_VideoInfo *info;
int sflag = 0;
while(argc--)
if(!strcmp(*argv++, "-s")) {
printf("save mode\n");
sflag = 1;
}
printf("ChromakeyMan 2.2\n");
printf("by 'Bl0ckeduser', 2007, 2008, 2012\n\n");
printf("Chromakey detection POWER (17000): "); scanf("%d", &dif);
printf("Chromakey FILL TOLERANCE (15): "); scanf("%d", &areatol);
adj: printf("Adjacency check ? (y/n): "); scanf("%s", &buf);
if(*buf == 'y') adjmode = 1;
else if(*buf == 'n') adjmode = 0;
else goto adj;
ltol: printf("Local tolerance ? (y/n): "); scanf("%s", &buf);
if(*buf == 'y') ltmode = 1;
else if(*buf == 'n') ltmode = 0;
else goto ltol;
if(ltmode) {
printf("Local color tolerance (3000): "); scanf("%d", &localtol);
}
/*
* Setup SDL video
*/
if(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTTHREAD) != 0)
fail("SDL initialization");
image = SDL_LoadBMP("F2.bmp");
if(!image)
fail("F2.bmp");
xl = image->w;
yl = image->h;
/*
* Allocate adjacency matrix
*/
matrix = malloc((yl+1) * sizeof(int **));
if(!matrix) fail("malloc matrix");
for(i = 0; i <= yl; i++)
{
matrix[i] = malloc((xl + 1) * sizeof(int *));
if(!matrix[i]) fail("malloc matrix[i]");
}
/*
* Setup SDL screen
*/
if(!(info = SDL_GetVideoInfo()))
fail("video info");
if(!(screen = SDL_SetVideoMode(xl, yl, info->vfmt->BitsPerPixel, SDL_DOUBLEBUF)))
fail("Screen creation");
/* Fill screen with white */
SDL_FillRect(screen, NULL, SDL_MapRGB(screen->format, 255, 255, 255));
/* Draw the initial frame */
SDL_BlitSurface(image, NULL, screen, NULL);
SDL_Flip(screen);
/*
* Wait for the user to click on a
* pixel part of the region to be
* keyed out.
*/
do
SDL_WaitEvent(&event);
while(event.type != SDL_MOUSEBUTTONDOWN);
mx = event.button.x;
my = event.button.y;
get_pixel(mx, my, &r, &g, &b);
privater=round(r/257.0f)*257;
privateg=round(g/257.0f)*257;
privateb=round(b/257.0f)*257;
fid=3;
xl=xl-1;
yl=yl-1;
CONSTANT :
ex=0;
ey=0;
/*
* Load a new frame if possible,
* otherwise stop.
*/
SDL_FreeSurface(image);
sprintf(buf, "F%d.bmp", fid);
if ((image = SDL_LoadBMP(buf))) {
SDL_BlitSurface(image, NULL, screen, NULL);
} else {
goto end;
}
/*
* Set up a blank adjacency matrix
* for this frame.
*/
for(i = 0; i <= yl; i++)
for(j = 0; j <= xl; j++)
matrix[i][j] = 0;
kc = 0;
/* Moyenne de couleur (color average) */
watr:
/* RED */
if (r>65534) {
rmn=r-dif;
rmp=0;
} else {
if (r<1) {
rmn=2000;
rmp=r+dif;
} else {
rmn=r-dif;
rmp=r+dif;
}
}
/* GREEN */
if (g>65534) {
gmn=g-dif;
gmp=0;
} else {
if (g<1) {
gmn=2000;
gmp=g+dif;
} else {
gmn=g-dif;
gmp=g+dif;
}
}
/* BLUE */
if (b>65534) {
bmn=b-dif;
bmp=0;
} else {
if (b<1) {
bmn=2000;
bmp=b+dif;
} else {
bmn=b-dif;
bmp=b+dif;
}
}
POSTER :
rcolr=65535;
rcolg=65535;
rcolb=65535;
get_pixel(ex, ey, &nr, &ng, &nb);
if(chk++ && ltmode) {
/*
* Check local RGB tolerance against
* left pixel
*/
if(abs(pr-nr) + abs(pg-ng) + abs(pb-nb) > localtol) {
ng = gmn-1; /* hack to choke test below */
}
}
pr = nr;
pg = ng;
pb = nb;
if(kc >= 5 && adjmode) {
/*
* Check for adjacent keyed-in
* pixel
*/
achk = 0;
if(ex > 0)
if(matrix[ey][ex - 1])
achk = 1;
if(ey > 0)
if(matrix[ey - 1][ex])
achk = 1;
if(ex > 0 && ey > 0)
if(matrix[ey - 1][ex - 1])
achk = 1;
if(!achk)
ng = gmn-1; /* hack to choke test below */
}
/* Reds posterize */
if (nr<rmp && nr>rmn && nb<bmp && nb>bmn && ng<gmp && ng>gmn) {
if (abs(ex-lvalid) < areatol) {
if(ex <= xl && ey <= yl)
matrix[ey][ex] = 1;
kc++;
forecolor(0, 65535, 0);
plot(ex, ey);
lvalid=ex;
rcolr=0;
rcolg=0;
rcolb=0;
}
}
ex=ex+1;
/*
* If we made it to the end of the
* line, do it again backwards
*/
if (ex>xl) {
lvalid=xl;
/* --- double pass ... ---*/
chk = 0;
do {
rcolr=65535;
rcolg=65535;
rcolb=65535;
get_pixel(ex, ey, &nr, &ng, &nb);
if(chk++ && ltmode) {
get_pixel(ex - 1, ey, &pr, &pg, &pb);
if(abs(pr-nr) + abs(pg-ng) + abs(pb-nb) > localtol) {
ng = gmn-1; /* hack to choke test below */
}
}
pr = nr;
pg = ng;
pb = nb;
if(kc >= 5 && adjmode) {
/*
* Check for adjacent keyed-in
* pixel, but backwards, since
* this pass goes backwards.
*/
achk = 0;
if(ex < xl - 1)
if(matrix[ey][ex + 1])
achk = 1;
if(ey < yl - 1)
if(matrix[ey + 1][ex])
achk = 1;
if(ex < xl - 1 && ey < yl - 1)
if(matrix[ey + 1][ex + 1])
achk = 1;
/*
* Also check forwards.
*/
if(ex > 0)
if(matrix[ey][ex - 1])
achk = 1;
if(ey > 0)
if(matrix[ey - 1][ex])
achk = 1;
if(ex > 0 && ey > 0)
if(matrix[ey - 1][ex - 1])
achk = 1;
if(!achk)
ng = gmn-1; /* hack to choke test below */
}
/* Reds posterize */
if (nr<rmp && nr>rmn && nb<bmp && nb>bmn && ng<gmp && ng>gmn) {
if (abs(ex-lvalid)<areatol) {
forecolor(0, 65535, 0);
plot(ex,ey);
lvalid=ex;
if(ex <= xl && ey <= yl)
matrix[ey][ex] = 1;
kc++;
rcolr=0;
rcolg=0;
rcolb=0;
}
}
ex=ex-1;
}
while(ex > 1);
/* --- double pass ... ---*/
lvalid=0;
ey=ey+1;
chk = 0;
}
if (ey>yl) {
goto ENDR;
}
goto watr;
/*
* Save the chromakeyed frame
* and jump to the code that
* checks for a next frame.
*/
ENDR :
SDL_Flip(screen);
if(sflag) {
sprintf(buf, "F%d.bmp", fid);
remove(buf);
if(SDL_SaveBMP(screen, buf))
fail("save bmp");
}
fid=fid+1;
goto CONSTANT;
end:
/*
* Clean up SDL stuff
*/
SDL_Quit();
SDL_FreeSurface(image);
return 0;
}
/*
* putpixel() and getpixel() are taken from
* examples in the SDL documentation, written
* by the SDL author, Sam Lantinga.
*/
/*
* Set the pixel at (x, y) to the given value
* NOTE: The surface must be locked before calling this!
*/
void putpixel(SDL_Surface *surface, int x, int y, Uint32 pixel)
{
int bpp = surface->format->BytesPerPixel;
/* Here p is the address to the pixel we want to set */
Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * bpp;
switch(bpp) {
case 1:
*p = pixel;
break;
case 2:
*(Uint16 *)p = pixel;
break;
case 3:
if(SDL_BYTEORDER == SDL_BIG_ENDIAN) {
p[0] = (pixel >> 16) & 0xff;
p[1] = (pixel >> 8) & 0xff;
p[2] = pixel & 0xff;
} else {
p[0] = pixel & 0xff;
p[1] = (pixel >> 8) & 0xff;
p[2] = (pixel >> 16) & 0xff;
}
break;
case 4:
*(Uint32 *)p = pixel;
break;
}
}
/*
* Return the pixel value at (x, y)
* NOTE: The surface must be locked before calling this!
*/
Uint32 getpixel(SDL_Surface *surface, int x, int y)
{
int bpp = surface->format->BytesPerPixel;
/* Here p is the address to the pixel we want to retrieve */
Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * bpp;
switch(bpp) {
case 1:
return *p;
case 2:
return *(Uint16 *)p;
case 3:
if(SDL_BYTEORDER == SDL_BIG_ENDIAN)
return p[0] << 16 | p[1] << 8 | p[2];
else
return p[0] | p[1] << 8 | p[2] << 16;
case 4:
return *(Uint32 *)p;
default:
return 0; /* shouldn't happen, but avoids warnings */
}
}
/*
* Emulation of METAL QuickDraw graphics
* API
*/
void plot(int x, int y)
{
Uint32 color = SDL_MapRGB(screen->format,
forecolor_r / 257,
forecolor_g / 257,
forecolor_b / 257);
if(x > xl || y > yl)
return;
putpixel(screen, x, y, color);
}
void forecolor(int r, int g, int b)
{
forecolor_r = r;
forecolor_g = g;
forecolor_b = b;
}
void get_pixel(int x, int y, int* r, int* g, int* b)
{
Uint8 ur, ug, ub;
Uint32 color;
if(x > xl || y > yl)
return;
color = getpixel(screen, x, y);
SDL_GetRGB(color, screen->format, &ur, &ug, &ub);
/* METAL Basic uses 0-65535 RGB range */
*r = 257 * ur;
*g = 257 * ug;
*b = 257 * ub;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment