图像渲染

#include "stdafx.h"
#include <SDL.h>
#include <SDL_image.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <float.h>
#include <limits.h>


#ifdef WIN32
#pragma comment(lib, "SDL.lib")
#pragma comment(lib, "SDLmain.lib")
#pragma comment(lib, "SDL_image.lib")
#endif


#define BRUTE       0x0
#define GAUSSIAN    0x1
#define AVG         0x2
#define ANN         0x4


#define YIQ         0x0
#define RGB         0x1


#define DIFF        0x0
#define PSRC        0x1


/* the images we load (or save, in case of g_B) */
static SDL_Surface * g_a = NULL;
static SDL_Surface * g_A = NULL;
static SDL_Surface * g_b = NULL;
static SDL_Surface * g_B = NULL;


/* our own pixel abstraction */
typedef struct 
{
    int x;
    int y;
} pixel;


/* our own point abstraction (this is really a feature vector and
 * probably should be an array, but since I know I'll be only dealing
 * with RGB (or YIQ) I only need to worry about 3 values) 
 */
typedef struct
{
    int x; /* the r or y */
    int y; /* the g or i */
    int z; /* the b or q */
} point;


/* our pyramids */
typedef struct 
{
    point *** points; /* triple pointer, of: level, row, column */
    int * h; /* array of height values per level */
    int * w; /* array of width values per level */
    pixel ** s;
} pyramid;


static pyramid g_pa;
static pyramid g_pb;
static pyramid g_pA;
static pyramid g_pB;


/* some globals */
static int g_seed = 42; /* seed for randomizing g_B's lowest level if wanted */
static int g_neigh_width = 5; /* width of the neighborhood */
static int g_pyramid_levels = 4; /* the number of pyramid levels, 0 indexed */
static int g_pyramid_type = BRUTE; /* the method used to generate higher pyramid 
                               levels */
static int g_rand = 0; /* whether we want to randomize the lowest level of g_B */
static int g_is_graphical = 1; /* whether the program runs in graphical mode */
static int g_color_mode = RGB; /* the color mode we want to run in (RGB requires 
                           3 times as many computations) */
static int g_passes = 1; /* number of passes to do at each pyramid level */
static int g_method_type = BRUTE; /* method to compute neighborhoods */
static int g_source = DIFF; /* method to calculate new pixel */
static double g_coherence = 0.0; /* for coherence checking */


void usage(char * execname);
void handle_opts(int argc, char ** argv);
void draw_images(SDL_Surface * screen);
SDL_Color get_pixel(SDL_Surface * surf, int x, int y);
void set_pixel(SDL_Surface * surf, int x, int y, SDL_Color color);
void draw_progress(SDL_Surface * screen, int pos);
void draw_bar(SDL_Surface * screen, int width);


void do_brute_analogy(SDL_Surface * screen);
pixel best_brute_match(pixel q, int l, int pass);
int do_dist(point a, point b, int weight);
int do_brute_dist(pixel p, pixel q, int l, int pass);
void do_diff(int l, pixel p, pixel q);


/* since the purpose of YIQ is speedup, we write a whole set of
 * essentially duplicate functions so we don't have to hit a boatload
 * of if statements everywhere
 */
void do_brute_analogy_YIQ(SDL_Surface * screen);
pixel best_brute_match_YIQ(pixel q, int l, int pass);
int do_dist_YIQ(point a, point b, int weight);
int do_brute_dist_YIQ(pixel p, pixel q, int l, int pass);
void do_diff_YIQ(int l, pixel p, pixel q);


void create_pyramids(int levels, int type, SDL_Surface * screen);
point get_point(pyramid p, int level, int x, int y);
void pyramid_init_brute(void);
void pyramid_init_avg(void);
void pyramid_init_gaussian(void);
point do_avg(pyramid p, int l, int x, int y);
point do_gauss(pyramid p, int l, int x, int y);


SDL_Color p_get_color(pyramid p, int level, int x, int y);
void draw_pyramid_level(SDL_Surface * screen, pyramid p, int level, int xoff, int yoff);
point to_YIQ(point color);
point to_RGB(point color);
void save_image(void);


int main(int argc, char ** argv)
{
    SDL_Surface * screen;


    fprintf(stdout, "Handling options & initializing...\n");
    handle_opts(argc, argv);
    srand((unsigned int)g_seed);
    fprintf(stdout, "Initializing SDL...\n");
        
    if (SDL_Init(SDL_INIT_VIDEO) != 0)
        perror("SDL Init failed");
    (void)atexit(SDL_Quit);


    if (g_is_graphical==1)
    {
        screen = SDL_SetVideoMode(800, 640, 16, SDL_DOUBLEBUF);


        if(screen == NULL)
            perror("SDL Set Video Mode failed"), exit(EXIT_FAILURE);
    }
    
    /* draw our images */
    fprintf(stdout, "Initializing pyramids\n");
    create_pyramids(g_pyramid_levels, g_pyramid_type, screen);
    fprintf(stdout, "Finished making pyramids\n");


    if (g_is_graphical==1)
    {
        fprintf(stdout, "Drawing initial pyramids\n");
        draw_images(screen);
        SDL_Flip(screen);
        fprintf(stdout, "Done drawing initial pyramids\n");
    }
    
    /* TODO: ANN? */
    
    switch (g_method_type)
    {
    case BRUTE:
        if (g_color_mode == RGB)
            do_brute_analogy(screen);
        else if (g_color_mode == YIQ)
            do_brute_analogy_YIQ(screen);
        break;
    case ANN:
        /* TODO: implement ANN */
        fprintf(stdout, "Sorry ANN currently not supported\n");
        break;
    }


    /* convert lowest level of g_pB to our image and save it */
    save_image();
    
    /* TODO: massive memory cleanup, for now let OS handle it */
    SDL_FreeSurface(g_a);
    SDL_FreeSurface(g_A);
    SDL_FreeSurface(g_b);
    SDL_FreeSurface(g_B);
    SDL_FreeSurface(screen);


    return EXIT_SUCCESS;
}


void
save_image()
{
    int x, y;
    SDL_Color col;


    fprintf(stdout, "Saving image as output.bmp\n");
    for (y=0; y<g_B->h; y++)
    {
        for (x=0; x<g_B->w; x++)
        {
            col = p_get_color(g_pB, 0, x, y);
            set_pixel(g_B, x, y, col);
        }
    }
    fprintf(stdout, "Done...\n");
    SDL_SaveBMP(g_B, "output.bmp");
}
/** 
 * Draws the initial images 
 * @param   screen  The screen we're drawing to.
 */
void
draw_images(SDL_Surface * screen)
{ /* {{{ */
    int l;
    int xoff, yoff;


    /* this won't handle the new YIQ colors :( 
    SDL_Rect src, dst;
    
    src.x = 0;
    src.y = 0;
    src.w = g_a->w;
    src.h = g_a->h;
    dst=src;
    SDL_BlitSurface(g_a, &src, screen, &dst);
   
    src.x = 0;
    src.y = 0;
    src.w = g_A->w;
    src.h = g_A->h;
    dst=src;
    dst.y = g_a->h + 10;
    SDL_BlitSurface(g_A, &src, screen, &dst);
    
    src.x = 0;
    src.y = 0;
    src.w = g_b->w;
    src.h = g_b->h;
    dst=src;
    dst.y = g_a->h+10+g_A->h+10;
    SDL_BlitSurface(g_b, &src, screen, &dst);
    
    src.x = 0;
    src.y = 0;
    src.w = g_B->w;
    src.h = g_B->h;
    dst=src;
    dst.y = g_a->h+10+g_A->h+10+g_b->h+10;


    SDL_BlitSurface(g_B, &src, screen, &dst);
    */
    xoff = 0;
    for (l = 0, yoff=0; l < g_pyramid_levels; l++)
    {
        draw_pyramid_level(screen, g_pa, l, xoff, yoff);
        yoff+=g_pa.h[l]+10;
    }
    xoff += g_pa.w[0] + 10;
    for (l = 0, yoff=0; l < g_pyramid_levels; l++)
    {
        draw_pyramid_level(screen, g_pA, l, xoff, yoff);
        yoff+=g_pA.h[l]+10;
    }
    xoff += g_pA.w[0] + 10;
    for (l = 0, yoff=0; l < g_pyramid_levels; l++)
    {
        draw_pyramid_level(screen, g_pb, l, xoff, yoff);
        yoff+=g_pb.h[l]+10;
    }
    xoff += g_pb.w[0] + 10;
    for (l = 0, yoff=0; l < g_pyramid_levels; l++)
    {
        draw_pyramid_level(screen, g_pB, l, xoff, yoff);
        yoff+=g_pB.h[l]+10;
    }
     
    SDL_Flip(screen);
    return;
} /* }}} */


/**
 * Handles our input options.
 *
 * @param   argc    the number of arguments
 * @param   argv    the actual arguments
 */
void 
handle_opts(int argc, char ** argv)
{ /* {{{ */
    int a;
    int x, y;
    SDL_Color col;
    
    while( (a=getopt(argc, argv, "Ghra:A:b:B:c:k:l:m:n:p:s:S:t:")) != EOF)
    {
        switch(a)
        {
        case 'G':
            g_is_graphical = 0;
            break;
        case 'h':
            usage(argv[0]);
            exit(EXIT_SUCCESS);
            /* NOT REACHED */
            break;
        case 'r':
            g_rand = 1;
            break;
        case 'a':
            fprintf(stdout, "Loading `%s' as image `a'\n", optarg);
            g_a = IMG_Load(optarg);
            break;
        case 'A':
            fprintf(stdout, "Loading `%s' as image `A'\n", optarg);
            g_A = IMG_Load(optarg);
            break;
        case 'b':
            fprintf(stdout, "Loading `%s' as image `b'\n", optarg);
            g_b = IMG_Load(optarg);
            g_B = IMG_Load(optarg);
            col.r = col.b = col.g = 0;
            for (y=0; y < g_B->h; y++)
            {
                for (x=0; x < g_B->w; x++)
                {
                    set_pixel( g_B, x, y, col);
                }
            }
            break;
        case 'B':
            g_B = IMG_Load(optarg);
            fprintf(stdout, "Loading `%s' as image `B'\n", optarg);
            g_rand = 0;
            break;
        case 'c':
            if (strncmp(optarg, "RGB", 3) == 0) g_color_mode = RGB;
            if (strncmp(optarg, "YIQ", 3) == 0) g_color_mode = YIQ;
            break;
        case 'k':
            g_coherence = atof(optarg);
            break;
        case 'l':
            g_pyramid_levels = atoi(optarg);
            break;
        case 'm':
            if (strncmp(optarg, "BRUTE", 5) == 0) g_method_type = BRUTE;
            if (strncmp(optarg, "ANN", 2) == 0) g_method_type = ANN;
            break;
        case 'n':
            g_neigh_width = atoi(optarg);
            break;
        case 'p':
            g_passes = atoi(optarg);
            break;
        case 's':
            g_seed = atoi(optarg);
            break;
        case 'S':
            if (strncmp(optarg, "PSRC", 4) == 0) g_source = PSRC;
            if (strncmp(optarg, "DIFF", 4) == 0) g_source = DIFF;
            break;
        case 't':
            if (strncmp(optarg, "BRUTE", 5) == 0) g_pyramid_type = BRUTE;
            if (strncmp(optarg, "AVG", 3) == 0) g_pyramid_type = AVG;
            if (strncmp(optarg, "GAUSSIAN", 8) == 0) g_pyramid_type = GAUSSIAN;
            break;
        case '?':
            /* 
             * getopt handles error reporting by default 
             */
            exit(EXIT_FAILURE);
            /* NOT REACHED */
        }
    }
    argc-=optind;
    argv+=optind;


    /* check for extra parameters */
    if (argc > 0)
    {
        fprintf(stderr, "ERROR: too many arguments\n");
        exit(EXIT_FAILURE);
        /* NOT REACHED */
    }
    if (g_a == NULL)
    {
        fprintf(stderr, "ERROR: image `a' not specified\n");
        exit(EXIT_FAILURE);
        /* NOT REACHED */
    }
    if (g_A == NULL)
    {
        fprintf(stderr, "ERROR: image `A' not specified\n");
        exit(EXIT_FAILURE);
        /* NOT REACHED */
    }
    if (g_b == NULL)
    {
        fprintf(stderr, "ERROR: image `b' not specified\n");
        exit(EXIT_FAILURE);
        /* NOT REACHED */
    }
    if (g_B == NULL)
    {
        fprintf(stderr, "ERROR: image `B' (noise) not specified\n");
        exit(EXIT_FAILURE);
        /* NOT REACHED */
    }
    if (g_neigh_width <= 0)
    {
        fprintf(stderr, "ERROR: neighborhood width must be positive!\n");
        exit(EXIT_FAILURE);
        /* NOT REACHED */
    }
    if (g_passes <= 0)
    {
        fprintf(stderr, "ERROR: passes must be positive\n");
        exit(EXIT_FAILURE);
    }
    /* do randomizing if desired */
    if (g_rand != 0)
    {
        for (y=0; y < g_B->h; y++)
        {
            for (x=0; x < g_B->w; x++)
            {
                col.r = (Uint8) (rand() % 256);
                col.b = (Uint8) (rand() % 256);
                col.g = (Uint8) (rand() % 256);
                set_pixel( g_B, x, y, col);
            }
        }
    }


    /* print what we're running with */
    if (g_is_graphical == 0)
        fprintf(stdout, "Running in non-gui mode\n");
    else
        fprintf(stdout, "Running in gui-mode\n");
    
    fprintf(stdout, "Using neighborhood width of %d\n", g_neigh_width);
    fprintf(stdout, "Pyramid Levels are %d\n", g_pyramid_levels);
    switch(g_color_mode)
    {
        case RGB:
            fprintf(stdout, "Running in RGB\n");
            break;
        case YIQ: 
            fprintf(stdout, "Running in YIQ\n");
            break;
    }
    fprintf(stdout, "Seed is %d\n", g_seed);
    fprintf(stdout, "Number of passes per pyramid level %d\n", g_passes);
    switch(g_pyramid_type)
    {
        case BRUTE:
            fprintf(stdout, "Pyramids constructed using BRUTE method\n");
            break;
        case AVG: 
            fprintf(stdout, "Pyramids constructed using AVG method\n");
            break;
        case GAUSSIAN: 
            fprintf(stdout, "Pyramids constructed using GAUSSIAN method\n");
            break;
    }
    if (g_rand==1) fprintf(stdout, "Pyramid for B randomly initializes\n");
    if (g_coherence>0.0) fprintf(stdout, "Doing coherence with k=%f\n",
            g_coherence);
    switch(g_source)
    {
        case DIFF:
            fprintf(stdout, "Matches used to compute difference\n");
            break;
        case PSRC: 
            fprintf(stdout, "Matches directly applied from A (a2) image\n");
            break;
    }


    return;
} /* }}} */


/**
 * Prints out a usage message
 * @param   execname    the name of the executable
 */
void
usage(char * execname)
{ /* {{{ */
    /* To be ISO compliant strings can't be longer than 509 chars */
    fprintf(stdout, 
            "USAGE: %s [options]\n",
            execname);
    fprintf(stdout,
            "[-G] run in non-gui mode\n"
            "[-h] displays this help menu\n"
            "[-P] if using DIFF, later passes than 1 drop multiplier\n"
            "[-a filename] specifies the a image (required)\n"
            "[-A filename] specifies the A image, i.e., changed a (required)\n"
            "[-b filename] specifies the b image (required)\n" 
            "[-B filename] specifies the noise to init the B image as (optional)\n"
            "[-c color] the color mode to run in (RGB, YIQ)\n");
    fprintf(stdout,
            "[-m method] method to search with (BRUTE, ...)\n"
            "[-n neighborhood width] allows to set the neigborhood width (must be odd)\n"
            "[-p passes] the number of passes to perform at each pyramid level\n"
            "[-r] init B with random noise from rand()\n"
            "[-s seed] allows to specify random seed used for the -r option\n\n"
            "[-S source] allows to specify how to treat best matches (DIFF, PSRC)\n\n"
            "[-t type] the way pyramids are constructed (BRUTE, AVG, GAUSSIAN)\n"
           );
} /* }}} */


/**
 * Sets a pixel in the surface (converts from YIQ if needed)
 * @param   surf    the surface we're writing to
 * @param   x       the x coordinate of the pixel to change
 * @param   y       the y coordinate of the pixel to change
 * @param   color   the color to change it to
 */
void 
set_pixel(SDL_Surface * surf, int x, int y, SDL_Color color)
{ /* {{{ */
    Uint32 col;
    char * pos;
    
    col = SDL_MapRGB(surf->format, color.r, color.g, color.b);
    pos = (char *) surf->pixels;
    pos += (surf->pitch * y);
    pos += (surf->format->BytesPerPixel * x);
    memcpy(pos, &col, surf->format->BytesPerPixel);
} /* }}} */


/**
 * Gets a pixel at a particular location (converts to YIQ if needed)
 * @param   surf    the surface we're getting the pixel from
 * @param   x       the x coordinate of the pixel to get
 * @param   y       the y coordinate of the pixel to get
 */
SDL_Color
get_pixel(SDL_Surface * surf, int x, int y)
{ /* {{{ */
    SDL_Color color;
    Uint32 col;
    char * pos;
    
    pos = (char *)surf->pixels;
    pos += (surf->pitch * y);
    pos += (surf->format->BytesPerPixel * x);
    
    memcpy(&col, pos, surf->format->BytesPerPixel);
    SDL_GetRGB(col, surf->format, &color.r, &color.g, &color.b);
    return color;
} /* }}} */


void
do_brute_analogy(SDL_Surface * screen)
{
    int l, x, y;
    int pass;
    pixel p, q;
    SDL_Event event;
    SDL_keysym keysym;
    int xoff, yoff;
    SDL_Color col;


    xoff = g_a->w + 10 + g_A->w + 10 + g_b->w + 10;


    for (l=g_pyramid_levels-1; l>=0; l--)
    {
        yoff = 0;
        for (x=0; x<l; x++)
            yoff+=g_pB.h[x]+10;
        pass = 0;
        for (y=0; y < g_pB.h[l]; y++)
        {
            for (x=0; x < g_pB.w[l]; x++)
            {
                q.x=x;  q.y=y;
                p = best_brute_match(q, l, pass);
                g_pB.points[l][x][y] = g_pA.points[l][p.x][p.y]; 
                if (g_source == DIFF)
                {
                    do_diff(l, p, q);
                }
                col = p_get_color(g_pB, l, x, y);
                if(g_is_graphical==1) set_pixel(screen, xoff + x, yoff+y, col);
                while (SDL_PollEvent(&event) != 0)
                {
                    switch (event.type)
                    {
                        case SDL_KEYDOWN:
                            keysym = event.key.keysym;


                            if (keysym.sym == SDLK_q)
                            {
                                fprintf(stdout, "Exiting\n");
                                save_image();
                                exit(EXIT_SUCCESS);
                            }
                            break;
                    }
                }
            }
            if(g_is_graphical==1)SDL_Flip(screen);
            else fprintf(stdout, "level: %d\tpass: %d\trow: %d of %d\n",
                    l, pass, y, g_pB.h[l]);
        }
        pass++;
        while (pass < g_passes)
        {
            for (y=0; y < g_pB.h[l]; y++)
            {
                for (x=0; x < g_pB.w[l]; x++)
                {
                    q.x=x;  q.y=y;
                    p = best_brute_match(q, l, pass);
                    g_pB.points[l][x][y] = g_pA.points[l][p.x][p.y]; 
                    if (g_source == DIFF)
                    {
                        do_diff(l, p, q);
                    }
                    col = p_get_color(g_pB, l, x, y);
                    if(g_is_graphical==1)set_pixel(screen, xoff + x, yoff+y, col);


                    while (SDL_PollEvent(&event) != 0)
                    {
                        switch (event.type)
                        {
                            case SDL_KEYDOWN:
                                keysym = event.key.keysym;


                                if (keysym.sym == SDLK_q)
                                {
                                    fprintf(stdout, "Exiting\n");
                                    save_image();
                                    exit(EXIT_SUCCESS);
                                }
                                break;
                        }
                    }
                }
                if(g_is_graphical==1)SDL_Flip(screen);
                else fprintf(stdout, "level: %d\tpass: %d\trow: %d of %d\n",
                        l, pass, y, g_pB.h[l]);
            }
            pass++;
        }
    }
    return;
}


void
do_diff(int l, pixel p, pixel q)
{
    g_pB.points[l][q.x][q.y].x = g_pb.points[l][q.x][q.y].x + 
        g_pA.points[l][p.x][p.y].x - g_pa.points[l][p.x][p.y].x;
    g_pB.points[l][q.x][q.y].y = g_pb.points[l][q.x][q.y].y + 
        g_pA.points[l][p.x][p.y].y - g_pa.points[l][p.x][p.y].y;
    g_pB.points[l][q.x][q.y].z = g_pb.points[l][q.x][q.y].z + 
        g_pA.points[l][p.x][p.y].z - g_pa.points[l][p.x][p.y].z;


    /* normalize */
    if (g_pB.points[l][q.x][q.y].x > 255) g_pB.points[l][q.x][q.y].x=255;
    else if(g_pB.points[l][q.x][q.y].x < 0) g_pB.points[l][q.x][q.y].x=0;
    if (g_pB.points[l][q.x][q.y].y > 255) g_pB.points[l][q.x][q.y].y=255;
    else if(g_pB.points[l][q.x][q.y].y < 0) g_pB.points[l][q.x][q.y].y=0;
    if (g_pB.points[l][q.x][q.y].z > 255) g_pB.points[l][q.x][q.y].z=255;
    else if(g_pB.points[l][q.x][q.y].z < 0) g_pB.points[l][q.x][q.y].z=0;
}


pixel
best_brute_match(pixel q, int l, int pass)
{
    pixel p; 
    pixel match; 
    int x, y;
    int best_dist = INT_MAX;
    int dist;
    /*
    int w = g_neigh_width / 2;
    pixel coh_match;
    int coh_dist;
    int best_coh_dist = INT_MAX;
    */
    /* go through all the pixels in example images (a, A) and find the
     * one that minimizes distance
     */
    match.x = 0;
    match.y = 0;
    for (x=0; x < g_pA.w[l]; x++)
    {
        for (y=0; y < g_pA.h[l]; y++)
        {
            p.x=x; p.y=y;
            dist = do_brute_dist(p, q, l, pass);
            if (dist < best_dist)
            {
                match = p;
                best_dist = dist;
            }
        }
    }
    /* TODO: do coherence */
    /*for (y = -w; y < w; y++)
    {
        for (x = -w; x < w; x++)
        {
            p.x=q.x + w; p.y=q.y + y;
            
            coh_dist = do_brute_dist(p, q, l, pass);
            if (coh_dist < best_coh_dist)
            {
                coh_match = p;
                best_coh_dist = dist;
            }
        }
    }*/
    return match;
}


int
do_brute_dist(pixel p, pixel q, int l, int pass)
{
    point a, A;
    point b, B;


    int dist = 0;
    int w = g_neigh_width / 2;
    int x, y;
    
    for (y = -w; y < w; y++)
    {
        for (x = -w; x < w; x++)
        {
            /* dist += get_ab_dist(p.x+x, p.y+y); */
            a = get_point(g_pa, l, p.x + x, p.y + y);
            b = get_point(g_pb, l, q.x + x, q.y + y);


            dist += do_dist(a, b, w);
            if (y > 0 && pass==0) continue;
            if (y == 0 && x >= 0 && pass==0) continue;
            
            A = get_point(g_pA, l, p.x + x, p.y + y);
            B = get_point(g_pB, l, q.x + x, q.y + y);
            
            dist += do_dist(A, B, w);
        }
    }
    /* handle one level up (if it exists) */
    if (l + 1 < g_pyramid_levels)
    {
        w/=2;
        
        for (y = -w; y < w; y++)
        {
            for (x = -w; x < w; x++)
            {
                /* dist += get_ab_dist(p.x+x, p.y+y); */
                a = get_point(g_pa, l+1, p.x/2 + x, p.y/2 + y);
                b = get_point(g_pb, l+1, q.x/2 + x, q.y/2 + y);


                dist += do_dist(a, b, w);


                A = get_point(g_pA, l+1, p.x/2 + x, p.y/2 + y);
                B = get_point(g_pB, l+1, q.x/2 + x, q.y/2 + y);


                dist += do_dist(A, B, w);
            }
        }
    }
    return dist;
}


int 
do_dist(point a, point b, int weight)
{
    return 
            ((a.x - b.x) * (a.x - b.x) +
            (a.y - b.y) * (a.y - b.y) +
            (a.z - b.z) * (a.z - b.z)) >> abs(weight);
}


void
do_brute_analogy_YIQ(SDL_Surface * screen)
{
    int l, x, y;
    int pass;
    pixel p, q;
    SDL_Event event;
    SDL_keysym keysym;
    int xoff, yoff;
    SDL_Color col;


    xoff = g_a->w + 10 + g_A->w + 10 + g_b->w + 10;


    for (l=g_pyramid_levels-1; l>=0; l--)
    {
        yoff = 0;
        for (x=0; x<l; x++)
            yoff+=g_pB.h[x]+10;
        pass = 0;
        for (y=0; y < g_pB.h[l]; y++)
        {
            for (x=0; x < g_pB.w[l]; x++)
            {
                q.x=x;  q.y=y;
                p = best_brute_match_YIQ(q, l, pass);
                g_pB.points[l][x][y] = g_pA.points[l][p.x][p.y]; 
                g_pB.points[l][x][y].y = g_pb.points[l][x][y].y;
                g_pB.points[l][x][y].z = g_pb.points[l][x][y].z;
                if (g_source == DIFF)
                {
                    do_diff_YIQ(l, p, q);
                }
                col = p_get_color(g_pB, l, x, y);
                set_pixel(screen, xoff + x, yoff+y, col);
                while (SDL_PollEvent(&event) != 0)
                {
                    switch (event.type)
                    {
                        case SDL_KEYDOWN:
                            keysym = event.key.keysym;


                            if (keysym.sym == SDLK_q)
                            {
                                fprintf(stdout, "Exiting\n");
                                save_image();
                                exit(EXIT_SUCCESS);
                            }
                            break;
                    }
                }
            }
            SDL_Flip(screen);
        }
        pass++;
        while (pass < g_passes)
        {
            for (y=0; y < g_pB.h[l]; y++)
            {
                for (x=0; x < g_pB.w[l]; x++)
                {
                    q.x=x;  q.y=y;
                    p = best_brute_match_YIQ(q, l, pass);
                    g_pB.points[l][x][y] = g_pA.points[l][p.x][p.y]; 
                    g_pB.points[l][x][y].y = g_pb.points[l][x][y].y;
                    g_pB.points[l][x][y].z = g_pb.points[l][x][y].z;
                    if (g_source == DIFF)
                    {
                        do_diff_YIQ(l, p, q);
                    }
                    col = p_get_color(g_pB, l, x, y);
                    set_pixel(screen, xoff + x, yoff+y, col);


                    while (SDL_PollEvent(&event) != 0)
                    {
                        switch (event.type)
                        {
                            case SDL_KEYDOWN:
                                keysym = event.key.keysym;


                                if (keysym.sym == SDLK_q)
                                {
                                    fprintf(stdout, "Exiting\n");
                                    save_image();
                                    exit(EXIT_SUCCESS);
                                }
                                break;
                        }
                    }
                }
                SDL_Flip(screen);
            }
            pass++;
        }
    }
    return;
}


void
do_diff_YIQ(int l, pixel p, pixel q)
{
    g_pB.points[l][q.x][q.y].x = g_pb.points[l][q.x][q.y].x + 
        g_pA.points[l][p.x][p.y].x - g_pa.points[l][p.x][p.y].x;
    g_pB.points[l][q.x][q.y].y = g_pb.points[l][q.x][q.y].y;
    g_pB.points[l][q.x][q.y].z = g_pb.points[l][q.x][q.y].z; 
    /*
    g_pB.points[l][q.x][q.y].y = g_pb.points[l][q.x][q.y].y + 
        g_pA.points[l][p.x][p.y].y - g_pa.points[l][p.x][p.y].y;
    g_pB.points[l][q.x][q.y].z = g_pb.points[l][q.x][q.y].z + 
        g_pA.points[l][p.x][p.y].z - g_pa.points[l][p.x][p.y].z;
    */


    /* normalize */
    /*
    if (g_pB.points[l][q.x][q.y].x > 255) g_pB.points[l][q.x][q.y].x=255;
    else if(g_pB.points[l][q.x][q.y].x < 0) g_pB.points[l][q.x][q.y].x=0;
    if (g_pB.points[l][q.x][q.y].y > 255) g_pB.points[l][q.x][q.y].y=255;
    else if(g_pB.points[l][q.x][q.y].y < 0) g_pB.points[l][q.x][q.y].y=0;
    if (g_pB.points[l][q.x][q.y].z > 255) g_pB.points[l][q.x][q.y].z=255;
    else if(g_pB.points[l][q.x][q.y].z < 0) g_pB.points[l][q.x][q.y].z=0;
    */
}


pixel
best_brute_match_YIQ(pixel q, int l, int pass)
{
    pixel p; 
    pixel match; 
    int x, y;
    int best_dist = INT_MAX;
    int dist;
    /*
    int w = g_neigh_width / 2;
    pixel coh_match;
    int coh_dist;
    int best_coh_dist = INT_MAX;
    */
    
    /* go through all the pixels in example images (a, A) and find the
     * one that minimizes distance
     */
    match.x = 0;
    match.y = 0;
    for (x=0; x < g_pA.w[l]; x++)
    {
        for (y=0; y < g_pA.h[l]; y++)
        {
            p.x=x; p.y=y;
            dist = do_brute_dist_YIQ(p, q, l, pass);
            if (dist < best_dist)
            {
                match = p;
                best_dist = dist;
            }
        }
    }
    /* do coherence */
    /*
    for (y = -w; y < w; y++)
    {
        for (x = -w; x < w; x++)
        {
            p.x=q.x + w; p.y=q.y + y;
            
            coh_dist = do_brute_dist_YIQ(p, q, l, pass);
            if (coh_dist < best_coh_dist)
            {
                coh_match = p;
                best_coh_dist = dist;
            }
        }
    }*/
    return match;
}


int
do_brute_dist_YIQ(pixel p, pixel q, int l, int pass)
{
    point a, A;
    point b, B;


    int dist = 0;
    int w = g_neigh_width / 2;
    int x, y;
    
    for (y = -w; y < w; y++)
    {
        for (x = -w; x < w; x++)
        {
            /* dist += get_ab_dist(p.x+x, p.y+y); */
            a = get_point(g_pa, l, p.x + x, p.y + y);
            b = get_point(g_pb, l, q.x + x, q.y + y);


            dist += do_dist_YIQ(a, b, w);
            if (y > 0 && pass==0) continue;
            if (y == 0 && x >= 0 && pass==0) continue;
            
            A = get_point(g_pA, l, p.x + x, p.y + y);
            B = get_point(g_pB, l, q.x + x, q.y + y);
            
            dist += do_dist_YIQ(A, B, w);
        }
    }
    /* handle one level up (if it exists) */
    if (l + 1 < g_pyramid_levels)
    {
        w/=2;
        
        for (y = -w; y < w; y++)
        {
            for (x = -w; x < w; x++)
            {
                /* dist += get_ab_dist(p.x+x, p.y+y); */
                a = get_point(g_pa, l+1, p.x/2 + x, p.y/2 + y);
                b = get_point(g_pb, l+1, q.x/2 + x, q.y/2 + y);


                dist += do_dist_YIQ(a, b, w);


                A = get_point(g_pA, l+1, p.x/2 + x, p.y/2 + y);
                B = get_point(g_pB, l+1, q.x/2 + x, q.y/2 + y);


                dist += do_dist_YIQ(A, B, w);
            }
        }
    }
    return dist;
}


int 
do_dist_YIQ(point a, point b, int weight)
{
    return ((a.x - b.x) * (a.x - b.x))>> abs(weight);
}


/** Create pyramids according to specified method */
void
create_pyramids(int levels, int type, SDL_Surface * screen)
{ /* {{{ */
    int x, y;
    SDL_Color col;
    
    /* allocate the heights and widths of our levels */
    if ((g_pa.h = (int *)malloc(sizeof(int) * levels)) == NULL)
        perror("malloc"), exit(EXIT_FAILURE);
    if ((g_pb.h = (int *)malloc(sizeof(int) * levels)) == NULL)
        perror("malloc"), exit(EXIT_FAILURE);
    if ((g_pA.h = (int *)malloc(sizeof(int) * levels)) == NULL)
        perror("malloc"), exit(EXIT_FAILURE);
    if ((g_pB.h = (int *)malloc(sizeof(int) * levels)) == NULL)
        perror("malloc"), exit(EXIT_FAILURE);
    if ((g_pa.w = (int *)malloc(sizeof(int) * levels)) == NULL)
        perror("malloc"), exit(EXIT_FAILURE);
    if ((g_pb.w = (int *)malloc(sizeof(int) * levels)) == NULL)
        perror("malloc"), exit(EXIT_FAILURE);
    if ((g_pA.w = (int *)malloc(sizeof(int) * levels)) == NULL)
        perror("malloc"), exit(EXIT_FAILURE);
    if ((g_pB.w = (int *)malloc(sizeof(int) * levels)) == NULL)
        perror("malloc"), exit(EXIT_FAILURE);
    
    /* init height and width info for all levels */
    for (x=0; x<levels; x++)
    {
        g_pa.h[x] = g_a->h >> x;
        g_pa.w[x] = g_a->w >> x;
        g_pb.h[x] = g_b->h >> x;
        g_pb.w[x] = g_b->w >> x;
        g_pA.h[x] = g_A->h >> x;
        g_pA.w[x] = g_A->w >> x;
        g_pB.h[x] = g_B->h >> x;
        g_pB.w[x] = g_B->w >> x;
    }
    
    /* allocate pointers to the appropriate levels */
    if ((g_pa.points = (point ***)malloc(sizeof(point **) * levels)) == NULL)
        perror("malloc"), exit(EXIT_FAILURE);
    for (x=0; x < levels; x++)
    {
        /* allocate the appropriate number of rows at each level */
        if((g_pa.points[x] = (point **)
                    malloc(sizeof(point *) * g_pa.w[0])) == NULL)
            perror("malloc"), exit(EXIT_FAILURE);
        /* allocate the appropriate number of columns at each level */
        for (y=0; y < g_pa.w[x]; y++)
            if((g_pa.points[x][y] = (point *)
                        malloc(sizeof(point) * g_pa.h[0])) == NULL)
                perror("malloc"), exit(EXIT_FAILURE);
    }
    if ((g_pb.points = (point ***)malloc(sizeof(point **) * levels)) == NULL)
        perror("malloc"), exit(EXIT_FAILURE);
    for (x=0; x < levels; x++)
    {
        if((g_pb.points[x] = (point **)
                    malloc(sizeof(point *) * g_pb.w[x])) == NULL)
            perror("malloc"), exit(EXIT_FAILURE);
        for (y=0; y < g_pb.w[x]; y++)
            if((g_pb.points[x][y] = (point *)
                        malloc(sizeof(point) * g_pb.h[x])) == NULL)
                perror("malloc"), exit(EXIT_FAILURE);
    }
    if ((g_pA.points = (point ***)malloc(sizeof(point **) * levels)) == NULL)
        perror("malloc"), exit(EXIT_FAILURE);
    for (x=0; x < levels; x++)
    {
        if((g_pA.points[x] = (point **)
                    malloc(sizeof(point *) * g_pA.w[x])) == NULL)
            perror("malloc"), exit(EXIT_FAILURE);
        for (y=0; y < g_pA.w[x]; y++)
            if((g_pA.points[x][y] = (point *)
                        malloc(sizeof(point) * g_pA.h[x])) == NULL)
                perror("malloc"), exit(EXIT_FAILURE);
    }
    if ((g_pB.points = (point ***)malloc(sizeof(point **) * levels)) == NULL)
        perror("malloc"), exit(EXIT_FAILURE);
    for (x=0; x < levels; x++)
    {
        if((g_pB.points[x] = (point **)
                    malloc(sizeof(point *) * g_pB.w[x])) == NULL)
            perror("malloc"), exit(EXIT_FAILURE);
        for (y=0; y < g_pB.w[x]; y++)
            if((g_pB.points[x][y] = (point *)
                        malloc(sizeof(point) * g_pB.h[x])) == NULL)
                perror("malloc"), exit(EXIT_FAILURE);
    }
        
    /* populate highest resolution (lowest level) of pyramids */
    for (y=0; y < g_pa.h[0]; y++)
    {
        for (x=0; x < g_pa.w[0]; x++)
        {
            col = get_pixel(g_a, x, y);
            g_pa.points[0][x][y].x = col.r;
            g_pa.points[0][x][y].y = col.g;
            g_pa.points[0][x][y].z = col.b;
            if (g_color_mode == YIQ)
                g_pa.points[0][x][y] = to_YIQ(g_pa.points[0][x][y]);
            /*
            set_pixel(screen, x, y, col);
            col = p_get_color(g_pa, 0, x, y);
            col.r = (Uint8) g_pa.points[0][x][y].x;
            col.g = (Uint8) g_pa.points[0][x][y].y;
            col.b = (Uint8) g_pa.points[0][x][y].z;
            set_pixel(screen, x+200, y, col);
        }
            SDL_Flip(screen);
            SDL_Delay(1);
            for (b=0; b < g_pa.h[0]; b++)
            {
                for (a=0; a <g_pa.w[0]; a++)
                {
                    col.r = (Uint8) g_pa.points[0][a][b].x;
                    col.g = (Uint8) g_pa.points[0][a][b].y;
                    col.b = (Uint8) g_pa.points[0][a][b].z;
                    set_pixel(screen, a+400, b, col);


                }
            }
            SDL_Flip(screen);*/
        }
    }
    for (y=0; y < g_b->h; y++)
    {
        for (x=0; x < g_b->w; x++)
        {
            col = get_pixel(g_b, x, y);
            g_pb.points[0][x][y].x = col.r;
            g_pb.points[0][x][y].y = col.g;
            g_pb.points[0][x][y].z = col.b;
            if (g_color_mode == YIQ)
                g_pb.points[0][x][y] = to_YIQ(g_pb.points[0][x][y]);
        }
    }
    for (y=0; y < g_A->h; y++)
    {
        for (x=0; x < g_A->w; x++)
        {
            col = get_pixel(g_A, x, y);
            g_pA.points[0][x][y].x = col.r;
            g_pA.points[0][x][y].y = col.g;
            g_pA.points[0][x][y].z = col.b;
            if (g_color_mode == YIQ)
                g_pA.points[0][x][y] = to_YIQ(g_pA.points[0][x][y]);
        }
    }
    for (y=0; y < g_B->h; y++)
    {
        for (x=0; x < g_B->w; x++)
        {
            col = get_pixel(g_B, x, y);
            g_pB.points[0][x][y].x = col.r;
            g_pB.points[0][x][y].y = col.g;
            g_pB.points[0][x][y].z = col.b;
            if (g_color_mode == YIQ)
                g_pB.points[0][x][y] = to_YIQ(g_pB.points[0][x][y]);
        }
    }


    /* create higher level pyramids */
    switch(type)
    {
    case BRUTE:
        pyramid_init_brute();
        break;
    case GAUSSIAN:
        pyramid_init_gaussian();
        break;
    case AVG:
        pyramid_init_avg();
        break;
    }
} /* }}} */


point
to_YIQ(point color)
{
    point tmp;


    tmp.x = (int)((.299 * color.x) + (.587 * color.y) + (.114 * color.z));
    tmp.y = (int)((.596 * color.x) - (.274 * color.y) - (.322 * color.z));
    tmp.z = (int)((.212 * color.x) - (.523 * color.y) + (.311 * color.z));
    if (tmp.x < 0) tmp.x = 0;
    else if(tmp.x > 255) tmp.x = 255;
    if (tmp.y < 0) tmp.y = 0;
    else if(tmp.y > 255) tmp.y = 255;
    if (tmp.z < 0) tmp.z = 0;
    else if(tmp.z > 255) tmp.z = 255;


    return tmp;
}


point
to_RGB(point color)
{
    point tmp;


    tmp.x = (int) (color.x + ( .956 * color.y) + ( .621 * color.z));
    tmp.y = (int) (color.x - ( .272 * color.y) - ( .647 * color.z));
    tmp.z = (int) (color.x - (1.105 * color.y) + (1.702 * color.z));


    return tmp;
}


/** initializes the upper pyramids by simply taking the corresponding
 * pixel at the upper level.  Dumb, and loses info.
 */
void
pyramid_init_brute(void)
{ /* {{{ */
    int l, x, y;
    int levels = g_pyramid_levels;


    for (l=1; l<levels; l++)
    {
        for (y=0; y < g_pa.h[l]; y++)
        {
            for (x=0; x < g_pa.w[l]; x++)
            {
                g_pa.points[l][x][y]=g_pa.points[l-1][x<<1][y<<1];
            }
        }
    }
    for (l=1; l<levels; l++)
    {
        for (y=0; y < g_pb.h[l]; y++)
        {
            for (x=0; x < g_pb.w[l]; x++)
            {
                g_pb.points[l][x][y]=g_pb.points[l-1][x<<1][y<<1];
            }
        }
    }
    for (l=1; l<levels; l++)
    {
        for (y=0; y < g_pA.h[l]; y++)
        {
            for (x=0; x < g_pA.w[l]; x++)
            {
                g_pA.points[l][x][y]=g_pA.points[l-1][x<<1][y<<1];
            }
        }
    }
    for (l=1; l<levels; l++)
    {
        for (y=0; y < g_pB.h[l]; y++)
        {
            for (x=0; x < g_pB.w[l]; x++)
            {
                g_pB.points[l][x][y]=g_pB.points[l-1][x<<1][y<<1];
            }
        }
    }
} /* }}} */


/** initializes the upper pyramids by simply taking the corresponding
 * average of the "cross" of pixels at the upper level.  
 */
void
pyramid_init_avg(void)
{ /* {{{ */
    int l, x, y;
    int levels = g_pyramid_levels;
    
    for (l=1; l<levels; l++)
    {
        for (y=0; y < g_pa.h[l]; y++)
        {
            for (x=0; x < g_pa.w[l]; x++)
            {
                g_pa.points[l][x][y] = do_avg(g_pa, l, x, y);
            }
        }
    }
    for (l=1; l<levels; l++)
    {
        for (y=0; y < g_pb.h[l]; y++)
        {
            for (x=0; x < g_pb.w[l]; x++)
            {
                g_pb.points[l][x][y] = do_avg(g_pb, l, x, y);
            }
        }
    }
    for (l=1; l<levels; l++)
    {
        for (y=0; y < g_pA.h[l]; y++)
        {
            for (x=0; x < g_pA.w[l]; x++)
            {
                g_pA.points[l][x][y] = do_avg(g_pA, l, x, y);
            }
        }
    }
    for (l=1; l<levels; l++)
    {
        for (y=0; y < g_pB.h[l]; y++)
        {
            for (x=0; x < g_pB.w[l]; x++)
            {
                g_pB.points[l][x][y] = do_avg(g_pB, l, x, y);
            }
        }
    }
} /* }}} */


point
do_avg(pyramid p, int l, int x, int y)
{ /* {{{ */
    point pts[9];
    point pt = {0, 0, 0};
    int lcv;
    
    pts[0] = get_point(p, l-1, x<<1     , y<<1);
    pts[1] = get_point(p, l-1, (x<<1)+1 , y<<1);
    pts[2] = get_point(p, l-1, x<<1     , (y<<1)+1);
    pts[3] = get_point(p, l-1, (x<<1)-1 , y<<1);
    pts[4] = get_point(p, l-1, x<<1     , (y<<1)-1);
    pts[5] = get_point(p, l-1, (x<<1)-1 , (y<<1)-1);
    pts[6] = get_point(p, l-1, (x<<1)+1 , (y<<1)-1);
    pts[7] = get_point(p, l-1, (x<<1)+1 , (y<<1)+1);
    pts[8] = get_point(p, l-1, (x<<1)-1 , (y<<1)+1);


    for(lcv = 0; lcv < 9; lcv++)
    {
        pt.x+=(int)(pts[lcv].x * .1/.9);
        pt.y+=(int)(pts[lcv].y * .1/.9);
        pt.z+=(int)(pts[lcv].z * .1/.9);
    }
    return pt;
} /* }}} */


/** initializes the upper pyramids by simply taking the corresponding
 * gaussian distribution of the "cross" of pixels at the upper level.  
 */
void
pyramid_init_gaussian(void)
{ /* {{{ */
    int l, x, y;
    int levels = g_pyramid_levels;
    
    for (l=1; l<levels; l++)
    {
        for (y=0; y < g_pa.h[l]; y++)
        {
            for (x=0; x < g_pa.w[l]; x++)
            {
                g_pa.points[l][x][y] = do_gauss(g_pa, l, x, y);
            }
        }
    }
    for (l=1; l<levels; l++)
    {
        for (y=0; y < g_pb.h[l]; y++)
        {
            for (x=0; x < g_pb.w[l]; x++)
            {
                g_pb.points[l][x][y] = do_gauss(g_pb, l, x, y);
            }
        }
    }
    for (l=1; l<levels; l++)
    {
        for (y=0; y < g_pA.h[l]; y++)
        {
            for (x=0; x < g_pA.w[l]; x++)
            {
                g_pA.points[l][x][y] = do_gauss(g_pA, l, x, y);
            }
        }
    }
    for (l=1; l<levels; l++)
    {
        for (y=0; y < g_pB.h[l]; y++)
        {
            for (x=0; x < g_pB.w[l]; x++)
            {
                g_pB.points[l][x][y] = do_gauss(g_pB, l, x, y);
            }
        }
    }
} /* }}} */


point
do_gauss(pyramid p, int l, int x, int y)
{ /* {{{ */
    point pts[9];
    point pt = {0, 0, 0};
    int lcv;
    
    pts[0] = get_point(p, l-1, x<<1     , y<<1);
    pts[1] = get_point(p, l-1, (x<<1)+1 , y<<1);
    pts[2] = get_point(p, l-1, x<<1     , (y<<1)+1);
    pts[3] = get_point(p, l-1, (x<<1)-1 , y<<1);
    pts[4] = get_point(p, l-1, x<<1     , (y<<1)-1);
    pts[5] = get_point(p, l-1, (x<<1)-1 , (y<<1)-1);
    pts[6] = get_point(p, l-1, (x<<1)+1 , (y<<1)-1);
    pts[7] = get_point(p, l-1, (x<<1)+1 , (y<<1)+1);
    pts[8] = get_point(p, l-1, (x<<1)-1 , (y<<1)+1);


    for(lcv = 1; lcv < 9; lcv++)
    {
        pt.x+=(int)(pts[lcv].x *.08);
        pt.y+=(int)(pts[lcv].y *.08);
        pt.z+=(int)(pts[lcv].z *.08);
    }
    pt.x+=(int)(pts[0].x *.36);
    pt.y+=(int)(pts[0].y *.36);
    pt.z+=(int)(pts[0].z *.36);
    return pt;
} /* }}} */


/** gets a point at a particular level in our pyramid, correctly
 * wrapping around for values that are too big or too small.
 */
point
get_point(pyramid p, int level, int x, int y)
{
    if (x < 0) 
    {
        for(; x < 0; x+=p.w[level]);
    }
    else if (x >= p.w[level]) x%= p.w[level];


    if (y < 0) 
    {
        for(; y < 0; y+=p.h[level]);
    }
    else if (y >= p.h[level]) y%= p.h[level];


    return p.points[level][x][y];
}


SDL_Color
p_get_color(pyramid p, int level, int x, int y)
{
    point pt;
    SDL_Color col;
    
    pt = get_point(p, level, x, y);
    if (g_color_mode == YIQ)
        pt = to_RGB(pt);
    col.r = (Uint8) (pt.x & 0x00FF);
    col.g = (Uint8) (pt.y & 0x00FF);
    col.b = (Uint8) (pt.z & 0x00FF);
    return col;
}


void
draw_pyramid_level(SDL_Surface * screen, pyramid p, int level, int xoff, int yoff)
{
    int x, y; 
    SDL_Color col;
    for (y=0; y<p.h[level]; y++)
    {
        for (x=0; x<p.w[level]; x++)
        {
            col = p_get_color(p, level, x, y);
            set_pixel(screen, x+xoff, y+yoff, col);
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值