Created
July 19, 2012 01:21
-
-
Save bl0ckeduser/3140147 to your computer and use it in GitHub Desktop.
ChromakeyMan v2.1
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* ChromakeyMan v2.1 | |
* 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 | |
* | |
* 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. | |
* | |
* Input: 1.bmp, 2.bmp, 3.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 $1 -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. | |
*/ | |
#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; | |
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.1\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); | |
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++) { | |
/* | |
* 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) { | |
/* | |
* 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++) { | |
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) { | |
/* | |
* 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