From 3afb02fc5d07a2b526e5a735a788fe70fc71a8ba Mon Sep 17 00:00:00 2001 From: giraffedata Date: Thu, 22 Feb 2007 03:44:54 +0000 Subject: Add -filter, -adaptive git-svn-id: http://svn.code.sf.net/p/netpbm/code/trunk@233 9d0c8265-081b-0410-96cb-a4ca84ce46f8 --- editor/pammixinterlace.c | 285 +++++++++++++++++++++++++++++++++++++---------- 1 file changed, 227 insertions(+), 58 deletions(-) (limited to 'editor/pammixinterlace.c') diff --git a/editor/pammixinterlace.c b/editor/pammixinterlace.c index 1421c7a2..579a8092 100644 --- a/editor/pammixinterlace.c +++ b/editor/pammixinterlace.c @@ -3,7 +3,7 @@ ******************************************************************************* De-interlace an image by merging adjacent rows. - Copyright (C) 2005 Bruce Guenter, FutureQuest, Inc. + Copyright (C) 2007 Bruce Guenter, FutureQuest, Inc. Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, @@ -14,32 +14,184 @@ ******************************************************************************/ -#include "pam.h" -#include "shhopt.h" +#include + #include "mallocvar.h" +#include "nstring.h" +#include "shhopt.h" +#include "pam.h" + + + +static sample +clamp(sample const val, + sample const maxval) { + + return val < 0 ? 0 : val > maxval ? maxval : val; +} + + + +static bool +distant(long const above, + long const mid, + long const below) { + + return abs(mid - (above + below) / 2) > abs(above - below); +} + + + +static void +filterLinearBlend(tuple * const outputrow, + tuple ** const tuplerowWindow, + unsigned int const width, + unsigned int const depth, + bool const adaptive, + sample const maxval) { + + unsigned int col; + + for (col = 0; col < width; ++col) { + unsigned int plane; + + for (plane = 0; plane < depth; ++plane) { + long const above = tuplerowWindow[0][col][plane]; + long const mid = tuplerowWindow[1][col][plane]; + long const below = tuplerowWindow[2][col][plane]; + + sample out; + + if (!adaptive || distant(above, mid, below)) + out = (above + mid * 2 + below) / 4; + else + out = mid; + + outputrow[col][plane] = out; + } + } +} + + + +static void +filterFfmpeg(tuple * const outputrow, + tuple ** const tuplerowWindow, + unsigned int const width, + unsigned int const depth, + bool const adaptive, + sample const maxval) { + + unsigned int col; + + for (col = 0; col < width; ++col) { + unsigned int plane; + + for (plane = 0; plane < depth; ++plane) { + long const above = tuplerowWindow[1][col][plane]; + long const mid = tuplerowWindow[2][col][plane]; + long const below = tuplerowWindow[3][col][plane]; + + sample out; + + if (!adaptive || distant(above, mid, below)) { + long const a = (- (long)tuplerowWindow[0][col][plane] + + above * 4 + + mid * 2 + + below * 4 + - (long)tuplerowWindow[4][col][plane]) / 8; + out = clamp(a, maxval); + } else + out = mid; + + outputrow[col][plane] = out; + } + } +} + + + +static void +filterFIR(tuple * const outputrow, + tuple ** const tuplerowWindow, + unsigned int const width, + unsigned int const depth, + bool const adaptive, + sample const maxval) { + + unsigned int col; + + for (col = 0; col < width; ++col) { + unsigned int plane; + + for (plane = 0; plane < depth; ++plane) { + + long const above = tuplerowWindow[1][col][plane]; + long const mid = tuplerowWindow[2][col][plane]; + long const below = tuplerowWindow[3][col][plane]; + + sample out; + + if (!adaptive || distant(above, mid, below)) { + long const a = (- (long)tuplerowWindow[0][col][plane] + + above * 2 + + mid * 6 + + below * 2 + - (long)tuplerowWindow[4][col][plane]) / 8; + out = clamp(a, maxval); + } else + out = mid; + + outputrow[col][plane] = out; + } + } +} + + + +struct filter { + const char * name; /* The command-line name of the filter */ + unsigned int rows; /* The number of rows the filter operates on */ + void (*filter)(tuple *, tuple **, + unsigned int width, unsigned int depth, + bool adaptive, sample maxval); +}; + +static struct filter filters[] = { + { "fir", 5, filterFIR }, /* FIR is cleanest and default filter */ + { "ffmpeg", 5, filterFfmpeg }, + { "linear", 3, filterLinearBlend }, +}; struct cmdlineInfo { /* All the information the user supplied in the command line, in a form easy for the program to use. */ - const char *inputFilespec; /* Filespecs of input files */ + const char * inputFileName; /* Names of input files */ + struct filter * filterP; + unsigned int adaptive; }; + static void parseCommandLine(int argc, char ** argv, - struct cmdlineInfo *cmdlineP) { + struct cmdlineInfo * const cmdlineP) { /*---------------------------------------------------------------------------- Note that the file spec array we return is stored in the storage that was passed to us as the argv array. -----------------------------------------------------------------------------*/ optStruct3 opt; /* set by OPTENT3 */ - optEntry *option_def; + optEntry * option_def; unsigned int option_def_index; + const char * filterName; + unsigned int filterSpec; MALLOCARRAY_NOFAIL(option_def, 100); option_def_index = 0; /* incremented by OPTENT3 */ + OPTENT3(0, "filter", OPT_STRING, &filterName, &filterSpec, 0); + OPTENT3(0, "adaptive", OPT_FLAG, NULL, &cmdlineP->adaptive, 0); opt.opt_table = option_def; opt.short_allowed = FALSE; /* We have no short (old-fashioned) options */ @@ -48,10 +200,25 @@ parseCommandLine(int argc, char ** argv, optParseOptions3(&argc, argv, opt, sizeof(opt), 0); /* Uses and sets argc, argv, and some of *cmdlineP and others. */ + if (!filterSpec) + cmdlineP->filterP = &filters[0]; + else { + unsigned int i; + cmdlineP->filterP = NULL; + for (i = 0; i < sizeof filters / sizeof(struct filter); ++i) { + if (STRCASEEQ(filterName, filters[i].name)) { + cmdlineP->filterP = &filters[i]; + break; + } + } + if (!cmdlineP->filterP) + pm_error("The filter name '%s' is not known.", filterName); + } + if (argc-1 < 1) - cmdlineP->inputFilespec = "-"; + cmdlineP->inputFileName = "-"; else if (argc-1 == 1) - cmdlineP->inputFilespec = argv[1]; + cmdlineP->inputFileName = argv[1]; else pm_error("You specified too many arguments (%d). The only " "argument is the optional input file specification.", @@ -62,40 +229,44 @@ parseCommandLine(int argc, char ** argv, static void allocateRowWindowBuffer(struct pam * const pamP, - tuple ** const tuplerow) { + tuple ** const tuplerowWindow, + unsigned int const rows) { unsigned int row; - for (row = 0; row < 3; ++row) - tuplerow[row] = pnm_allocpamrow(pamP); + for (row = 0; row < rows; ++row) + tuplerowWindow[row] = pnm_allocpamrow(pamP); } static void -freeRowWindowBuffer(tuple ** const tuplerow) { +freeRowWindowBuffer(tuple ** const tuplerowWindow, + unsigned int const rows) { unsigned int row; - for (row = 0; row < 3; ++row) - pnm_freepamrow(tuplerow[row]); - + for (row = 0; row < rows; ++row) + pnm_freepamrow(tuplerowWindow[row]); } static void -slideWindowDown(tuple ** const tuplerow) { +slideWindowDown(tuple ** const tuplerowWindow, + unsigned int const rows) { /*---------------------------------------------------------------------------- - Slide the 3-line tuple row window tuplerow[] down one row by moving + Slide the rows-line tuple row window tuplerowWindow[] down one by moving pointers. - - tuplerow[2] ends up an uninitialized buffer. -----------------------------------------------------------------------------*/ - tuple * const oldrow0 = tuplerow[0]; - tuplerow[0] = tuplerow[1]; - tuplerow[1] = tuplerow[2]; - tuplerow[2] = oldrow0; + tuple * const oldrow0 = tuplerowWindow[0]; + + unsigned int i; + + for (i = 0; i < rows-1; ++i) + tuplerowWindow[i] = tuplerowWindow[i+1]; + + tuplerowWindow[i] = oldrow0; } @@ -107,14 +278,17 @@ main(int argc, char *argv[]) { struct cmdlineInfo cmdline; struct pam inpam; struct pam outpam; - tuple * tuplerow[3]; + tuple * tuplerowWindow[5]; tuple * outputrow; + unsigned int rows; - pnm_init( &argc, argv ); + pnm_init(&argc, argv); parseCommandLine(argc, argv, &cmdline); - ifP = pm_openr(cmdline.inputFilespec); + rows = cmdline.filterP->rows; + + ifP = pm_openr(cmdline.inputFileName); pnm_readpaminit(ifP, &inpam, PAM_STRUCT_SIZE(tuple_type)); @@ -123,48 +297,43 @@ main(int argc, char *argv[]) { pnm_writepaminit(&outpam); - allocateRowWindowBuffer(&inpam, tuplerow); + allocateRowWindowBuffer(&inpam, tuplerowWindow, rows); outputrow = pnm_allocpamrow(&outpam); - if (inpam.height < 3) { + if (inpam.height < rows) { unsigned int row; - pm_message("WARNING: Image height less than 3. No mixing done."); + pm_message("WARNING: Image height less than %d. No mixing done.", + rows); for (row = 0; row < inpam.height; ++row) { - pnm_readpamrow(&inpam, tuplerow[0]); - pnm_writepamrow(&outpam, tuplerow[0]); + pnm_readpamrow(&inpam, tuplerowWindow[0]); + pnm_writepamrow(&outpam, tuplerowWindow[0]); } } else { unsigned int row; - pnm_readpamrow(&inpam, tuplerow[0]); - pnm_readpamrow(&inpam, tuplerow[1]); - - /* Pass through first row */ - pnm_writepamrow(&outpam, tuplerow[0]); - - for (row = 2; row < inpam.height; ++row) { - unsigned int col; - pnm_readpamrow(&inpam, tuplerow[2]); - for (col = 0; col < inpam.width; ++col) { - unsigned int plane; - - for (plane = 0; plane < inpam.depth; ++plane) { - outputrow[col][plane] = - (tuplerow[0][col][plane] - + tuplerow[1][col][plane] * 2 - + tuplerow[2][col][plane]) / 4; - } - } - pnm_writepamrow(&outpam, outputrow); - - slideWindowDown(tuplerow); - } - - /* Pass through last row */ - pnm_writepamrow(&outpam, tuplerow[1]); + for (row = 0; row < rows-1; ++row) + pnm_readpamrow(&inpam, tuplerowWindow[row]); + + /* Pass through first unfilterable rows */ + for (row = 0; row < rows/2; ++row) + pnm_writepamrow(&outpam, tuplerowWindow[row]); + + for (row = rows / 2 + 1; row < inpam.height - rows / 2 + 1; ++row) { + pnm_readpamrow(&inpam, tuplerowWindow[rows-1]); + cmdline.filterP->filter(outputrow, tuplerowWindow, + inpam.width, inpam.depth, + cmdline.adaptive, inpam.maxval); + pnm_writepamrow(&outpam, outputrow); + + slideWindowDown(tuplerowWindow, rows); + } + + /* Pass through last rows */ + for (row = rows/2; row < rows-1; ++row) + pnm_writepamrow(&outpam, tuplerowWindow[row]); } - freeRowWindowBuffer(tuplerow); + freeRowWindowBuffer(tuplerowWindow, rows); pnm_freepamrow(outputrow); pm_close(inpam.file); pm_close(outpam.file); -- cgit 1.4.1