static void
thresholdSimple(struct pam * const inpamP,
                struct pam * const outpamP,
                float        const threshold) {

    tuplen * inrow;    /* normalized input row */
    tuple * outrow;    /* raw output row */
    unsigned int row;  /* number of the current row */

    inrow  = pnm_allocpamrown(inpamP);
    outrow = pnm_allocpamrow(outpamP);

    /* do the simple thresholding */
    for (row = 0; row < inpamP->height; ++row) {
        unsigned int col;
        pnm_readpamrown(inpamP, inrow);
        for (col = 0; col < inpamP->width; ++col) {
            thresholdPixel(outpamP, inrow[col], outrow[col], threshold);
        }
        pnm_writepamrow(outpamP, outrow);
    }

    pnm_freepamrow(inrow);
    pnm_freepamrow(outrow);
}
Exemple #2
0
static void
composite(int          const originleft, 
          int          const origintop, 
          struct pam * const underlayPamP,
          struct pam * const overlayPamP,
          struct pam * const alphaPamP,
          bool         const invertAlpha,
          float        const masterOpacity,
          struct pam * const composedPamP,
          bool         const assumeLinear,
          bool         const mixTransparency) {
/*----------------------------------------------------------------------------
   Overlay the overlay image in the array 'overlayImage', described by
   *overlayPamP, onto the underlying image from the input image file
   as described by *underlayPamP, output the composite to the image
   file as described by *composedPamP.

   Apply the overlay image with transparency described by the array
   'alpha' and *alphaPamP.

   The underlying image is positioned after its header.

   'originleft' and 'origintop' are the coordinates in the underlying
   image plane where the top left corner of the overlay image is to
   go.  It is not necessarily inside the underlying image (in fact,
   may be negative).  Only the part of the overlay that actually
   intersects the underlying image, if any, gets into the output.

   We assume that the span from the topmost row of the two images to
   the bottommost row is less than INT_MAX.
-----------------------------------------------------------------------------*/
    enum sampleScale const sampleScale = 
        assumeLinear ? INTENSITY_SAMPLE : GAMMA_SAMPLE;
    enum alphaMix const alphaMix =
        mixTransparency ? AM_OVERLAY : AM_KEEPUNDER;

    int underlayRow;  /* NB may be negative */
    int overlayRow;   /* NB may be negative */
    tuple * composedTuplerow;
    tuple * underlayTuplerow;
    struct pam adaptUnderlayPam;
    tuple * overlayTuplerow;
    struct pam adaptOverlayPam;
    tuplen * alphaTuplerown;

    composedTuplerow = pnm_allocpamrow(composedPamP);
    underlayTuplerow = pnm_allocpamrow(underlayPamP);
    overlayTuplerow  = pnm_allocpamrow(overlayPamP);
    if (alphaPamP)
        alphaTuplerown = pnm_allocpamrown(alphaPamP);
    else
        alphaTuplerown = NULL;

    determineInputAdaptations(underlayPamP, overlayPamP, composedPamP,
                              &adaptUnderlayPam, &adaptOverlayPam);

    pnm_writepaminit(composedPamP);

    assert(INT_MAX - overlayPamP->height > origintop); /* arg constraint */

    for (underlayRow = MIN(0, origintop), overlayRow = MIN(0, -origintop);
         underlayRow < MAX(underlayPamP->height, 
                           origintop + overlayPamP->height);
         ++underlayRow, ++overlayRow) {

        if (overlayRow >= 0 && overlayRow < overlayPamP->height) {
            pnm_readpamrow(overlayPamP, overlayTuplerow);
            adaptRowFormat(overlayPamP, &adaptOverlayPam, overlayTuplerow);
            if (alphaPamP)
                pnm_readpamrown(alphaPamP, alphaTuplerown);
        }
        if (underlayRow >= 0 && underlayRow < underlayPamP->height) {
            pnm_readpamrow(underlayPamP, underlayTuplerow);
            adaptRowFormat(underlayPamP, &adaptUnderlayPam, underlayTuplerow);
            if (underlayRow < origintop || 
                underlayRow >= origintop + overlayPamP->height) {
            
                /* Overlay image does not touch this underlay row. */

                pnm_writepamrow(composedPamP, underlayTuplerow);
            } else {
                composeRow(originleft, &adaptUnderlayPam, &adaptOverlayPam,
                           invertAlpha, masterOpacity, 
                           composedPamP, sampleScale, alphaMix,
                           underlayTuplerow, overlayTuplerow, alphaTuplerown,
                           composedTuplerow);
                
                pnm_writepamrow(composedPamP, composedTuplerow);
            }
        }
    }
    pnm_freepamrow(composedTuplerow);
    pnm_freepamrow(underlayTuplerow);
    pnm_freepamrow(overlayTuplerow);
    if (alphaPamP)
        pnm_freepamrown(alphaTuplerown);
}
int
main(int argc, char *argv[]) {

    struct cmdlineInfo cmdline;
    FILE* ifP;

    pgm_init(&argc, argv);

    parseCommandLine(argc, argv, &cmdline);

    srand(cmdline.randomseedSpec ? cmdline.randomseed : pm_randseed());

    ifP = pm_openr(cmdline.inputFilespec);

    if (cmdline.halftone == QT_HILBERT)
        doHilbert(ifP, cmdline.clumpSize);
    else {
        struct converter converter;
        struct pam graypam;
        struct pam bitpam;
        tuplen * grayrow;
        tuple * bitrow;
        int row;

        pnm_readpaminit(ifP, &graypam, PAM_STRUCT_SIZE(tuple_type));

        bitpam = makeOutputPam(graypam.width, graypam.height);
        
        pnm_writepaminit(&bitpam);

        switch (cmdline.halftone) {
        case QT_FS:
            converter = createFsConverter(&graypam, cmdline.threshval);
            break;
        case QT_ATKINSON:
            converter = createAtkinsonConverter(&graypam, cmdline.threshval);
            break;
        case QT_THRESH:
            converter = createThreshConverter(&graypam, cmdline.threshval);
            break;
        case QT_DITHER8: 
            converter = createClusterConverter(&graypam, DT_REGULAR, 8); 
            break;
        case QT_CLUSTER: 
            converter = createClusterConverter(&graypam, 
                                               DT_CLUSTER, 
                                               cmdline.clusterRadius);
            break;
        case QT_HILBERT: 
                pm_error("INTERNAL ERROR: halftone is QT_HILBERT where it "
                         "shouldn't be.");
                break;
        }

        grayrow = pnm_allocpamrown(&graypam);
        bitrow  = pnm_allocpamrow(&bitpam);

        for (row = 0; row < graypam.height; ++row) {
            pnm_readpamrown(&graypam, grayrow);

            converter.convertRow(&converter, row, grayrow, bitrow);
            
            pnm_writepamrow(&bitpam, bitrow);
        }
        pnm_freepamrow(bitrow);
        pnm_freepamrow(grayrow);

        if (converter.destroy)
            converter.destroy(&converter);
    }

    pm_close(ifP);

    return 0;
}
Exemple #4
0
static void
composite(int          const originleft, 
          int          const origintop, 
          struct pam * const underlayPamP,
          struct pam * const overlayPamP,
          struct pam * const alphaPamP,
          bool         const invertAlpha,
          float        const masterOpacity,
          struct pam * const composedPamP,
          bool         const assumeLinear) {
/*----------------------------------------------------------------------------
   Overlay the overlay image in the array 'overlayImage', described by
   *overlayPamP, onto the underlying image from the input image file
   as described by *underlayPamP, output the composite to the image
   file as described by *composedPamP.

   Apply the overlay image with transparency described by the array
   'alpha' and *alphaPamP.

   The underlying image is positioned after its header.

   'originleft' and 'origintop' are the coordinates in the underlying
   image plane where the top left corner of the overlay image is to
   go.  It is not necessarily inside the underlying image (in fact,
   may be negative).  Only the part of the overlay that actually
   intersects the underlying image, if any, gets into the output.
-----------------------------------------------------------------------------*/
    enum sampleScale const sampleScale = 
        assumeLinear ? INTENSITY_SAMPLE : GAMMA_SAMPLE;

    int underlayRow;  /* NB may be negative */
    int overlayRow;   /* NB may be negative */
    tuple * composedTuplerow;
    tuple * underlayTuplerow;
    tuple * overlayTuplerow;
    tuplen * alphaTuplerown;
    bool overlayHasOpacity;
    unsigned int opacityPlane;

    pnm_getopacity(overlayPamP, &overlayHasOpacity, &opacityPlane);

    composedTuplerow = pnm_allocpamrow(composedPamP);
    underlayTuplerow = pnm_allocpamrow(underlayPamP);
    overlayTuplerow  = pnm_allocpamrow(overlayPamP);
    if (alphaPamP)
        alphaTuplerown = pnm_allocpamrown(alphaPamP);

    pnm_writepaminit(composedPamP);

    for (underlayRow = MIN(0, origintop), overlayRow = MIN(0, -origintop);
         underlayRow < MAX(underlayPamP->height, 
                           origintop + overlayPamP->height);
         ++underlayRow, ++overlayRow) {

        if (overlayRow >= 0 && overlayRow < overlayPamP->height) {
            pnm_readpamrow(overlayPamP, overlayTuplerow);
            adaptRowToOutputFormat(overlayPamP, overlayTuplerow, composedPamP);
            if (alphaPamP)
                pnm_readpamrown(alphaPamP, alphaTuplerown);
        }
        if (underlayRow >= 0 && underlayRow < underlayPamP->height) {
            pnm_readpamrow(underlayPamP, underlayTuplerow);
            adaptRowToOutputFormat(underlayPamP, underlayTuplerow, 
                                   composedPamP);

            if (underlayRow < origintop || 
                underlayRow >= origintop + overlayPamP->height) {
            
                /* Overlay image does not touch this underlay row. */

                pnm_writepamrow(composedPamP, underlayTuplerow);
            } else {
                unsigned int col;
                for (col = 0; col < composedPamP->width; ++col) {
                    int const ovlcol = col - originleft;

                    if (ovlcol >= 0 && ovlcol < overlayPamP->width) {
                        tuplen const alphaTuplen = 
                            alphaPamP ? alphaTuplerown[ovlcol] : NULL;

                        overlayPixel(overlayTuplerow[ovlcol], overlayPamP,
                                     underlayTuplerow[col], underlayPamP,
                                     alphaTuplen, invertAlpha,
                                     overlayHasOpacity, opacityPlane,
                                     composedTuplerow[col], composedPamP,
                                     masterOpacity, sampleScale);
                    } else
                        /* Overlay image does not touch this column. */
                        pnm_assigntuple(composedPamP, composedTuplerow[col],
                                        underlayTuplerow[col]);
                }
                pnm_writepamrow(composedPamP, composedTuplerow);
            }
        }
    }
    pnm_freepamrow(composedTuplerow);
    pnm_freepamrow(underlayTuplerow);
    pnm_freepamrow(overlayTuplerow);
    if (alphaPamP)
        pnm_freepamrown(alphaTuplerown);
}
static void
thresholdLocal(struct pam *       const inpamP,
               struct pam *       const outpamP,
               struct cmdlineInfo const cmdline) {
/*----------------------------------------------------------------------------
  Threshold the image described by *inpamP, whose file is positioned to the
  raster, and output the resulting raster to the image described by
  *outpamP.

  Use local adaptive thresholding aka dynamic thresholding or dual
  thresholding (global for low contrast areas, LAT otherwise)
-----------------------------------------------------------------------------*/
    struct range globalRange; /* Range of sample values in entire image */
    tuplen ** inrows;
        /* vertical window of image containing the local area.  This is
           a ring of 'windowHeight' rows.  Row R of the image, when it is
           in the window, is inrows[R % windowHeight].
        */
    unsigned int windowHeight;  /* size of 'inrows' window */
    unsigned int nextRowToRead;
        /* Number of the next row to be read from the file into the inrows[]
           buffer.
        */
    tuple * outrow;           /* raw output row */
    unsigned int row;
        /* Number of the current row.  The current row is normally the
           one in the center of the inrows[] buffer (which has an actual
           center row because it is of odd height), but when near the top
           and bottom edge of the image, it is not.
        */
    const unsigned int * histogram;
    samplen globalThreshold;
        /* This is a threshold based on the entire image, to use in areas
           where the contrast is too small to use a locally-derived threshold.
        */
    unsigned int oddLocalWidth;
    unsigned int oddLocalHeight;
    unsigned int i;
    
    /* use a subimage with odd width and height to have a middle pixel */

    if (cmdline.width % 2 == 0)
        oddLocalWidth = cmdline.width + 1;
    else 
        oddLocalWidth = cmdline.width;
    if (cmdline.height % 2 == 0)
        oddLocalHeight = cmdline.height + 1;
    else
        oddLocalHeight = cmdline.height;

    windowHeight = MIN(oddLocalHeight, inpamP->height);

    /* global information is needed for dual thresholding */
    if (cmdline.dual) {
        analyzeDistribution(inpamP, cmdline.verbose, &histogram, &globalRange);
        computeGlobalThreshold(inpamP, histogram, globalRange,
                               &globalThreshold);
    } else {
        histogram = NULL;
        initRange(&globalRange);
        globalThreshold = 1.0;
    }

    outrow = pnm_allocpamrow(outpamP);

    MALLOCARRAY(inrows, windowHeight);

    if (inrows == NULL)
        pm_error("Unable to allocate memory for a %u-row array", windowHeight);

    for (i = 0; i < windowHeight; ++i)
        inrows[i] = pnm_allocpamrown(inpamP);

    /* Fill the vertical window buffer */
    nextRowToRead = 0;

    while (nextRowToRead < windowHeight)
        pnm_readpamrown(inpamP, inrows[nextRowToRead++ % windowHeight]);

    for (row = 0; row < inpamP->height; ++row) {
        thresholdLocalRow(inpamP, inrows, oddLocalWidth, windowHeight, row,
                          cmdline, globalRange, globalThreshold,
                          outpamP, outrow);

        pnm_writepamrow(outpamP, outrow);
        
        /* read next image line if available and necessary */
        if (row + windowHeight / 2 >= nextRowToRead &&
            nextRowToRead < inpamP->height)
            pnm_readpamrown(inpamP, inrows[nextRowToRead++ % windowHeight]);
    }

    free((void*)histogram);
    for (i = 0; i < windowHeight; ++i)
        pnm_freepamrow(inrows[i]);
    free(inrows);
    pnm_freepamrow(outrow);
}