From fe23dec1315a4dacaaf7c2932c510c5878f17f8e Mon Sep 17 00:00:00 2001 From: giraffedata Date: Tue, 14 Dec 2010 03:04:57 +0000 Subject: Add pammosaicknit git-svn-id: http://svn.code.sf.net/p/netpbm/code/trunk@1377 9d0c8265-081b-0410-96cb-a4ca84ce46f8 --- analyzer/pammosaicknit.c | 253 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 253 insertions(+) create mode 100644 analyzer/pammosaicknit.c (limited to 'analyzer/pammosaicknit.c') diff --git a/analyzer/pammosaicknit.c b/analyzer/pammosaicknit.c new file mode 100644 index 00000000..f0e4c7f9 --- /dev/null +++ b/analyzer/pammosaicknit.c @@ -0,0 +1,253 @@ +/* ---------------------------------------------------------------------- + * + * Validate a mosaic knitting pattern + * + * By Scott Pakin + * + * ---------------------------------------------------------------------- + * + * Copyright (C) 2010 Scott Pakin + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + * + * ---------------------------------------------------------------------- + */ + +#include +#include +#include +#include "mallocvar.h" +#include "pam.h" + +int const max_skips = 3; /* Maximum number of consecutive skips */ + +/* Each pixel can be either black or white and can be valid or invalid. */ +typedef enum { + MK_VALID_BLACK, + MK_VALID_WHITE, + MK_INVALID_BLACK, + MK_INVALID_WHITE +} mkPixelType; + + +static void +initializeInvalidColors(struct pam * const pamP, + tuple * const blackColorP, + tuple * const whiteColorP) { + tuple invalidBlack; + tuple invalidWhite; + + invalidBlack = pnm_allocpamtuple(pamP); + invalidBlack[PAM_RED_PLANE] = pamP->maxval/3; + invalidBlack[PAM_GRN_PLANE] = 0; + invalidBlack[PAM_BLU_PLANE] = 0; + *blackColorP = invalidBlack; + + invalidWhite = pnm_allocpamtuple(pamP); + invalidWhite[PAM_RED_PLANE] = pamP->maxval; + invalidWhite[PAM_GRN_PLANE] = pamP->maxval*3/4; + invalidWhite[PAM_BLU_PLANE] = pamP->maxval*3/4; + *whiteColorP = invalidWhite; +} + + + +static void +pamRowToKnitRow(struct pam * const pamP, + const tuple * const pamRow, + mkPixelType * const knitRow) { + + if (pamP->depth == 3) { + /* Convert from RGB. */ + unsigned int col; + + for (col = 0; col < pamP->width; ++col) { + double luminosity; + + luminosity = + pamRow[col][0]*pnm_lumin_factor[0] + + pamRow[col][1]*pnm_lumin_factor[1] + + pamRow[col][2]*pnm_lumin_factor[2]; + if (luminosity < pamP->maxval/2.0) + knitRow[col] = MK_VALID_BLACK; + else + knitRow[col] = MK_VALID_WHITE; + } + } + else { + /* Convert from either grayscale or black and white. */ + unsigned int col; + + for (col = 0; col < pamP->width; ++col) { + if (pamRow[col][0]*2 < pamP->maxval) + knitRow[col] = MK_VALID_BLACK; + else + knitRow[col] = MK_VALID_WHITE; + } + } +} + + + +static void +knitRowToPamRow(struct pam * const pamP, + const mkPixelType * const knitRow, + tuple * const pamRow, + tuple const invalidBlack, + tuple const invalidWhite) { + + tuple validBlack; + tuple validWhite; + unsigned int col; + + validBlack = pnm_allocpamtuple(pamP); + pnm_createBlackTuple(pamP, &validBlack); + validWhite = pnm_allocpamtuple(pamP); + validWhite[PAM_RED_PLANE] = pamP->maxval; + validWhite[PAM_GRN_PLANE] = pamP->maxval; + validWhite[PAM_BLU_PLANE] = pamP->maxval; + + for (col = 0; col < pamP->width; ++col) { + switch (knitRow[col]) { + case MK_VALID_BLACK: + pnm_assigntuple(pamP, pamRow[col], validBlack); + break; + + case MK_VALID_WHITE: + pnm_assigntuple(pamP, pamRow[col], validWhite); + break; + + case MK_INVALID_BLACK: + pnm_assigntuple(pamP, pamRow[col], invalidBlack); + break; + + case MK_INVALID_WHITE: + pnm_assigntuple(pamP, pamRow[col], invalidWhite); + break; + + default: + abort(); + break; + } + } + + pnm_freepamtuple(validBlack); + pnm_freepamtuple(validWhite); +} + + + +static void +validateMosaicPattern(struct pam * const inPamP, + struct pam * const outPamP) { +/* -------------------------------------------------------------------------- + Validate a mosaic knitting pattern. A valid pattern starts with a + "black" row on the bottom and alternates "white" and "black" rows. + A "black" row can contain any arrangement of black pixels but no + more than max_skips consecutive white pixels. A "white" row can + contain any arrangement of white pixels but no more than max_skips + consecutive black pixels. Columns wrap horizontally, so that + characteristic is taken into consideration when tallying + consecutive pixels. Invalid pixels are flagged with a red hue. It + is assumed that the input image is fairly small (on the order of a + few tens of pixels in each dimension), so red pixels should stand + out fairly well when the image is zoomed in. +--------------------------------------------------------------------------*/ + + tuple * inPamRow; /* One row of data read from the input file */ + mkPixelType * knitRow; /* Same as inPamRow but expressed as stitches */ + tuple * outPamRow; /* One row of data to write to the output file */ + tuple invalidBlack; /* Color representing an invalid black input */ + tuple invalidWhite; /* Color representing an invalid white input */ + mkPixelType rowColor; /* Base color of the current row */ + unsigned int row; + + inPamRow = pnm_allocpamrow(inPamP); + outPamRow = pnm_allocpamrow(outPamP); + MALLOCARRAY(knitRow, inPamP->width); + initializeInvalidColors(outPamP, &invalidBlack, &invalidWhite); + + rowColor = inPamP->height % 2 == 0 ? MK_VALID_WHITE : MK_VALID_BLACK; + for (row = 0; row < inPamP->height; ++row) { + unsigned int col; + + pnm_readpamrow(inPamP, inPamRow); + pamRowToKnitRow(inPamP, inPamRow, knitRow); + for (col = 0; col < inPamP->width; ++col) { + if (knitRow[col] != rowColor) { + unsigned int runLength = 0; /* Number of consecutive skips */ + + for (runLength = 0; + runLength < inPamP->width && + knitRow[(col+runLength)%inPamP->width] != rowColor; + ++runLength) + ; + if (runLength > max_skips) { + /* We have too many skips in a row -- mark them + with the "invalid" color. */ + unsigned int badOffset; + mkPixelType badColor; + + badColor = rowColor == MK_VALID_WHITE ? + MK_INVALID_BLACK : MK_INVALID_WHITE; + for (badOffset = 0; badOffset < runLength; ++badOffset) { + knitRow[(col+badOffset)%inPamP->width] = badColor; + } + } + col += runLength - 1; + } + } + knitRowToPamRow(outPamP, knitRow, outPamRow, + invalidBlack, invalidWhite); + pnm_writepamrow(outPamP, outPamRow); + rowColor = rowColor == MK_VALID_BLACK ? + MK_VALID_WHITE : MK_VALID_BLACK; + } + + free(knitRow); + pnm_freepamrow(outPamRow); + pnm_freepamrow(inPamRow); +} + + + +int +main(int argc, const char *argv[]) { + struct pam inPam; + struct pam outPam; + const char * inputFilename; + FILE * inFileP; + + pm_proginit(&argc, argv); + + inputFilename = (argc > 1) ? argv[1] : "-"; + inFileP = pm_openr(inputFilename); + + pnm_readpaminit(inFileP, &inPam, PAM_STRUCT_SIZE(tuple_type)); + + outPam = inPam; + outPam.file = stdout; + outPam.format = PAM_FORMAT; + outPam.depth = 3; + outPam.maxval = 255; + outPam.bytes_per_sample = 1; + strcpy(outPam.tuple_type, PAM_PPM_TUPLETYPE); + pnm_writepaminit(&outPam); + + validateMosaicPattern(&inPam, &outPam); + + pm_closer(inFileP); + return 0; +} + -- cgit 1.4.1