📄 video_util_resize.cpp
字号:
/* * Filtered Image Rescaling * Additional changes by Ray Gardener, Daylon Graphics Ltd. December 4, 1999 Summary: - Horizontal filter contributions are calculated on the fly, as each column is mapped from src to dst image. This lets us omit having to allocate a temporary full horizontal stretch of the src image. - If none of the src pixels within a sampling region differ, then the output pixel is forced to equal (any of) the source pixel. This ensures that filters do not corrupt areas of constant color. - Filter weight contribution results, after summing, are rounded to the nearest pixel color value instead of being casted to Pixel (usually an int or char). Otherwise, artifacting occurs. - All memory allocations checked for failure; scale() returns error code. new_image() returns NULL if unable to allocate pixel storage, even if Image struct can be allocated. Some assertions added. - load_image(), save_image() take filenames, not file handles. - TGA bitmap format available. If you want to add a filetype, extend the gImageHandlers array, and factor in your load_image_xxx() and save_image_xxx() functions. Search for string 'add your' to find appropriate code locations. - The 'input' and 'output' command-line arguments do not have to specify .bm files; any supported filetype is okay. - Added implementation of getopt() if compiling under Windows.*/#include "mp4live.h"#include "video_util_resize.h"#ifndef M_PI#define M_PI 3.14159265358979323846#endif#define WHITE_PIXEL (255)#define BLACK_PIXEL (0)#define CLAMP(v) (((v) < (BLACK_PIXEL)) ? \ (BLACK_PIXEL) : \ (((v) > (WHITE_PIXEL)) ? (WHITE_PIXEL) : (v)))#define get_pixel(image, y, x) ((image)->data[(x) + (y) * image->span])#define put_pixel(image, y, x, p) ((image)->data[(x) + (y) * image->span] = (p))/* Helper data structures: */typedef struct { int pixel; fixdouble weight;} CONTRIB;typedef struct { int n; /* number of contributors */ CONTRIB *p; /* pointer to list of contributions */} CLIST;void scale_setup_image(image_t *img, int w, int h, int depth, pixel_t *data){ img->xsize = h; img->ysize = w; img->pixspan = depth; img->span = w * depth; img->data = data;}image_t *scale_new_image(int w, int h, int depth) /* create a blank image */{ image_t *image; if((image = (image_t *)malloc(sizeof(image_t)))) { image->xsize = h; image->ysize = w; image->span = w * depth; image->pixspan = depth; image->data = NULL; } return image;}void scale_free_image(image_t *image){ free(image);}/* * filter function definitions */double Hermite_filter(double t){ // f(t) = 2|t|^3 - 3|t|^2 + 1, -1 <= t <= 1 if(t < 0.0) t = -t; if(t < 1.0) return((2.0 * t - 3.0) * t * t + 1.0); return(0.0);}double Box_filter(double t){ if((t > -0.5) && (t <= 0.5)) return(1.0); return(0.0);}double Triangle_filter(double t){ if(t < 0.0) t = -t; if(t < 1.0) return(1.0 - t); return(0.0);}double Bell_filter(double t) /* box (*) box (*) box */{ if(t < 0) t = -t; if(t < .5) return(.75 - (t * t)); if(t < 1.5) { t = (t - 1.5); return(.5 * (t * t)); } return(0.0);}double B_spline_filter(double t) /* box (*) box (*) box (*) box */{ double tt; if(t < 0) t = -t; if(t < 1) { tt = t * t; return((.5 * tt * t) - tt + (2.0 / 3.0)); } else if(t < 2) { t = 2 - t; return((1.0 / 6.0) * (t * t * t)); } return(0.0);}static double sinc(double x){ x *= M_PI; if(x != 0) return(sin(x) / x); return(1.0);}double Lanczos3_filter(double t){ if(t < 0) t = -t; if(t < 3.0) return(sinc(t) * sinc(t/3.0)); return(0.0);}#define B (1.0 / 3.0)#define C (1.0 / 3.0)double Mitchell_filter(double t){ double tt; tt = t * t; if(t < 0) t = -t; if(t < 1.0) { t = (((12.0 - 9.0 * B - 6.0 * C) * (t * tt)) + ((-18.0 + 12.0 * B + 6.0 * C) * tt) + (6.0 - 2 * B)); return(t / 6.0); } else if(t < 2.0) { t = (((-1.0 * B - 6.0 * C) * (t * tt)) + ((6.0 * B + 30.0 * C) * tt) + ((-12.0 * B - 48.0 * C) * t) + (8.0 * B + 24 * C)); return(t / 6.0); } return(0.0);}/* * image rescaling routine *//* calc_x_contrib( CLIST* contribX; * Receiver of contrib info double xscale; * Horizontal scaling double fwidth; * Filter sampling width int dstwidth; * Target bitmap width int srcwidth; * Source bitmap width double (*filterf)(double); * Filter proc int i; * pixel_t column in source bitmap being processed ) Calculates the filter weights for a single target column. contribX->p must be freed afterwards. Returns -1 if error, 0 otherwise.*/static int calc_x_contrib(CLIST* contribX, double xscale, double fwidth, int dstwidth, int srcwidth, double (*filterf)(double), int i){ double width; double fscale; double center, left, right; double weight; int j, k, n; if(xscale < 1.0) { /* Shrinking image */ width = fwidth / xscale; fscale = 1.0 / xscale; contribX->n = 0; contribX->p = (CONTRIB *)calloc((int) (width * 2 + 1), sizeof(CONTRIB)); if(contribX->p == NULL) return -1; center = (double) i / xscale; left = ceil(center - width); right = floor(center + width); for(j = (int)left; j <= right; ++j) { weight = center - (double) j; weight = (*filterf)(weight / fscale) / fscale; if(j < 0) n = -j; else if(j >= srcwidth) n = (srcwidth - j) + srcwidth - 1; else n = j; k = contribX->n++; contribX->p[k].pixel = n; contribX->p[k].weight = double2fixdouble(weight); } } else { /* Expanding image */ contribX->n = 0; contribX->p = (CONTRIB *)calloc((int) (fwidth * 2 + 1), sizeof(CONTRIB)); if(contribX->p == NULL) return -1; center = (double) i / xscale; left = ceil(center - fwidth); right = floor(center + fwidth); for(j = (int)left; j <= right; ++j) { weight = center - (double) j; weight = (*filterf)(weight); if(j < 0) { n = -j; } else if(j >= srcwidth) { n = (srcwidth - j) + srcwidth - 1; } else { n = j; } k = contribX->n++; contribX->p[k].pixel = n; contribX->p[k].weight = double2fixdouble(weight); } } return 0;} /* calc_x_contrib */scaler_t *scale_image_init(image_t *dst, image_t *src, double (*filterf)(double), double fwidth){ scaler_t *scaler; int i, j, k; /* loop variables */ int n; /* pixel number */ int xx; double center = 0.0, left, right; /* filter calculation variables */ double width, fscale, weight; /* filter calculation variables */ double xscale, yscale; double maxwidth; CLIST contribX; CLIST *contribY; instruction_t *prg; scaler = (scaler_t*)malloc(sizeof(scaler_t)); scaler->src = src; scaler->dst = dst; /* create intermediate column to hold horizontal dst column scale */ scaler->tmp = (pixel_t*)malloc(src->ysize * sizeof(pixel_t)); if(scaler->tmp == NULL) { free(scaler); return 0; } xscale = (double) dst->xsize / (double) src->xsize; /* Build y weights */ /* pre-calculate filter contributions for a column */ contribY = (CLIST *)calloc(dst->ysize, sizeof(CLIST)); if(contribY == NULL) { free(scaler->tmp); free(scaler); return 0; } yscale = (double) dst->ysize / (double) src->ysize; if(yscale < 1.0) { width = fwidth / yscale; fscale = 1.0 / yscale; for(i = 0; i < dst->ysize; ++i) { contribY[i].n = 0; contribY[i].p = (CONTRIB *)calloc((int) (width * 2 + 1), sizeof(CONTRIB)); if(contribY[i].p == NULL) { free(scaler->tmp); free(contribY); free(scaler); return 0; } center = (double) i / yscale; left = ceil(center - width); right = floor(center + width); for(j = (int)left; j <= right; ++j) { weight = center - (double) j; weight = (*filterf)(weight / fscale) / fscale; if(j < 0) { n = -j; } else if(j >= src->ysize) { n = (src->ysize - j) + src->ysize - 1; } else { n = j; } k = contribY[i].n++; contribY[i].p[k].pixel = n; contribY[i].p[k].weight = double2fixdouble(weight); } } } else { for(i = 0; i < dst->ysize; ++i) { contribY[i].n = 0; contribY[i].p = (CONTRIB *)calloc((int) (fwidth * 2 + 1), sizeof(CONTRIB)); if(contribY[i].p == NULL) { free(scaler->tmp); free(scaler); return 0; } center = (double) i / yscale; left = ceil(center - fwidth); right = floor(center + fwidth); for(j = (int)left; j <= right; ++j) { weight = center - (double) j; weight = (*filterf)(weight); if(j < 0) { n = -j; } else if(j >= src->ysize) { n = (src->ysize - j) + src->ysize - 1; } else { n = j; } k = contribY[i].n++; contribY[i].p[k].pixel = n; contribY[i].p[k].weight = double2fixdouble(weight); } } } /* ------------------------------------------------------------- streamline contributions into two simple bytecode programs. This single optimalization nearly doubles the performance! */ maxwidth = fwidth; if (xscale < 1.0 || yscale < 1.0) maxwidth = fwidth / (xscale < yscale ? xscale : yscale); prg = scaler->programX = (instruction_t*)calloc(scaler->dst->xsize * (2 + 2*(int)(maxwidth*2+1)), sizeof(instruction_t)); for(xx = 0; xx < scaler->dst->xsize; xx++) { calc_x_contrib(&contribX, xscale, fwidth, scaler->dst->xsize, scaler->src->xsize, filterf, xx); /*pel = get_pixel(scaler->src, contribX.p[0].pixel, k);*/ (prg++)->index = contribX.p[0].pixel * scaler->src->span; (prg++)->count = contribX.n; for(j = 0; j < contribX.n; ++j) { /*pel2 = get_pixel(scaler->src, contribX.p[j].pixel, k);*/ (prg++)->index = contribX.p[j].pixel * scaler->src->span; /*weight += pel2 * contribX.p[j].weight;*/ (prg++)->weight = contribX.p[j].weight; } free(contribX.p); } prg = scaler->programY = (instruction_t*)calloc(scaler->dst->ysize * (2 + 2*(int)(maxwidth*2+1)), sizeof(instruction_t)); /* The temp column has been built. Now stretch it vertically into dst column. */ for(i = 0; i < scaler->dst->ysize; ++i) { /**(prg++) = scaler->contribY[i].p[0].pixel;*/ (prg++)->pixel = scaler->tmp + contribY[i].p[0].pixel; (prg++)->count = contribY[i].n; for(j = 0; j < contribY[i].n; ++j) { /*(prg++) = scaler->contribY[i].p[j].pixel;*/ (prg++)->pixel = scaler->tmp + contribY[i].p[j].pixel; (prg++)->weight = contribY[i].p[j].weight; } } /* next dst row */ /* --------------------------------------------------- free the memory allocated for vertical filter weights -- no longer needed, we have programX and programY */ for(i = 0; i < scaler->dst->ysize; ++i) free(contribY[i].p); free(contribY); return scaler;}void scale_image_process(scaler_t *scaler){ int x; int i = 0, j, k; /* loop variables */ pixel_t pel, pel2; int bPelDelta; fixdouble weight; pixel_t *in; pixel_t *out = scaler->dst->data; pixel_t *tmpOut; instruction_t *prgY, *prgX = NULL, *prgXa; prgXa = scaler->programX; for(x = scaler->dst->xsize; x; --x) { /* Apply horz filter to make dst column in tmp. */ for(in = scaler->src->data, tmpOut = scaler->tmp, k = scaler->src->ysize; k; --k, in++) { prgX = prgXa; weight = 0; bPelDelta = false; pel = in[(prgX++)->index]; for(j = (prgX++)->count; j; --j) { pel2 = in[(prgX++)->index]; if(pel2 != pel) { bPelDelta = true; } weight += pel2 * (prgX++)->weight; } *(tmpOut++) = bPelDelta ? (pixel_t)CLAMP(fixdouble2int(weight)) : pel; } /* next row in temp column */ prgXa = prgX; /* * The temp column has been built. * Now stretch it vertically into dst column. */ prgY = scaler->programY; for(i = scaler->dst->ysize; i; --i) { weight = 0; bPelDelta = false; pel = *((prgY++)->pixel); for(j = (prgY++)->count; j; --j) { pel2 = *((prgY++)->pixel); if(pel2 != pel) { bPelDelta = true; } weight += pel2 * (prgY++)->weight; } *(out++) = bPelDelta ? (pixel_t)CLAMP(fixdouble2int(weight)) : pel; } /* next dst row */ } /* next dst column */}void scale_image_done(scaler_t *scaler){ free(scaler->tmp); free(scaler->programY); free(scaler->programX); free(scaler);}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -