about summary refs log tree commit diff
path: root/editor/pammixinterlace.c
diff options
context:
space:
mode:
authorgiraffedata <giraffedata@9d0c8265-081b-0410-96cb-a4ca84ce46f8>2007-02-22 03:44:54 +0000
committergiraffedata <giraffedata@9d0c8265-081b-0410-96cb-a4ca84ce46f8>2007-02-22 03:44:54 +0000
commit3afb02fc5d07a2b526e5a735a788fe70fc71a8ba (patch)
treeed1272b0bbfc0898d0acb45dc0ddb5bc343c5378 /editor/pammixinterlace.c
parent4f47609af73c4a7f9d222f355db7095e0351d2de (diff)
downloadnetpbm-mirror-3afb02fc5d07a2b526e5a735a788fe70fc71a8ba.tar.gz
netpbm-mirror-3afb02fc5d07a2b526e5a735a788fe70fc71a8ba.tar.xz
netpbm-mirror-3afb02fc5d07a2b526e5a735a788fe70fc71a8ba.zip
Add -filter, -adaptive
git-svn-id: http://svn.code.sf.net/p/netpbm/code/trunk@233 9d0c8265-081b-0410-96cb-a4ca84ce46f8
Diffstat (limited to 'editor/pammixinterlace.c')
-rw-r--r--editor/pammixinterlace.c285
1 files changed, 227 insertions, 58 deletions
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 <string.h>
+
 #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);